/* * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ package sun.jvm.hotspot; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; import sun.jvm.hotspot.ci.ciEnv; import sun.jvm.hotspot.code.CodeBlob; import sun.jvm.hotspot.code.CodeCacheVisitor; import sun.jvm.hotspot.code.NMethod; import sun.jvm.hotspot.debugger.Address; import sun.jvm.hotspot.debugger.OopHandle; import sun.jvm.hotspot.memory.SymbolTable; import sun.jvm.hotspot.memory.SystemDictionary; import sun.jvm.hotspot.memory.Universe; import sun.jvm.hotspot.oops.DefaultHeapVisitor; import sun.jvm.hotspot.oops.HeapVisitor; import sun.jvm.hotspot.oops.InstanceKlass; import sun.jvm.hotspot.oops.Klass; import sun.jvm.hotspot.oops.Metadata; import sun.jvm.hotspot.oops.Method; import sun.jvm.hotspot.oops.MethodData; import sun.jvm.hotspot.oops.Oop; import sun.jvm.hotspot.oops.RawHeapVisitor; import sun.jvm.hotspot.oops.Symbol; import sun.jvm.hotspot.oops.UnknownOopException; import sun.jvm.hotspot.opto.Compile; import sun.jvm.hotspot.opto.InlineTree; import sun.jvm.hotspot.runtime.CompiledVFrame; import sun.jvm.hotspot.runtime.CompilerThread; import sun.jvm.hotspot.runtime.JavaThread; import sun.jvm.hotspot.runtime.JavaVFrame; import sun.jvm.hotspot.runtime.Threads; import sun.jvm.hotspot.runtime.VM; import sun.jvm.hotspot.tools.ObjectHistogram; import sun.jvm.hotspot.tools.PMap; import sun.jvm.hotspot.tools.PStack; import sun.jvm.hotspot.tools.StackTrace; import sun.jvm.hotspot.tools.jcore.ClassDump; import sun.jvm.hotspot.tools.jcore.ClassFilter; import sun.jvm.hotspot.types.CIntegerType; import sun.jvm.hotspot.types.Field; import sun.jvm.hotspot.types.Type; import sun.jvm.hotspot.types.basic.BasicType; import sun.jvm.hotspot.ui.classbrowser.HTMLGenerator; import sun.jvm.hotspot.ui.tree.CTypeTreeNodeAdapter; import sun.jvm.hotspot.ui.tree.OopTreeNodeAdapter; import sun.jvm.hotspot.ui.tree.SimpleTreeNode; import sun.jvm.hotspot.utilities.AddressOps; import sun.jvm.hotspot.utilities.Assert; import sun.jvm.hotspot.utilities.HeapProgressThunk; import sun.jvm.hotspot.utilities.LivenessPathElement; import sun.jvm.hotspot.utilities.MethodArray; import sun.jvm.hotspot.utilities.ObjectReader; import sun.jvm.hotspot.utilities.PointerFinder; import sun.jvm.hotspot.utilities.PointerLocation; import sun.jvm.hotspot.utilities.ReversePtrs; import sun.jvm.hotspot.utilities.ReversePtrsAnalysis; import sun.jvm.hotspot.utilities.RobustOopDeterminator; import sun.jvm.hotspot.utilities.SystemDictionaryHelper; import sun.jvm.hotspot.utilities.soql.JSJavaFactory; import sun.jvm.hotspot.utilities.soql.JSJavaFactoryImpl; import sun.jvm.hotspot.utilities.soql.JSJavaScriptEngine; public class CommandProcessor { volatile boolean quit; public abstract static class DebuggerInterface { public abstract HotSpotAgent getAgent(); public abstract boolean isAttached(); public abstract void attach(String pid); public abstract void attach(String java, String core); public abstract void detach(); public abstract void reattach(); } public static class BootFilter implements ClassFilter { public boolean canInclude(InstanceKlass kls) { return kls.getClassLoader() == null; } } public static class NonBootFilter implements ClassFilter { private HashMap emitted = new HashMap(); public boolean canInclude(InstanceKlass kls) { if (kls.getClassLoader() == null) return false; if (emitted.get(kls.getName()) != null) { // Since multiple class loaders are being shoved // together duplicate classes are a possibilty. For // now just ignore them. return false; } emitted.put(kls.getName(), kls); return true; } } static class Tokens { final String input; int i; String[] tokens; int length; String[] splitWhitespace(String cmd) { String[] t = cmd.split("\\s"); if (t.length == 1 && t[0].length() == 0) { return new String[0]; } return t; } void add(String s, ArrayList t) { if (s.length() > 0) { t.add(s); } } Tokens(String cmd) { input = cmd; // check for quoting int quote = cmd.indexOf('"'); ArrayList t = new ArrayList(); if (quote != -1) { while (cmd.length() > 0) { if (quote != -1) { int endquote = cmd.indexOf('"', quote + 1); if (endquote == -1) { throw new RuntimeException("mismatched quotes: " + input); } String before = cmd.substring(0, quote).trim(); String quoted = cmd.substring(quote + 1, endquote); cmd = cmd.substring(endquote + 1).trim(); if (before.length() > 0) { String[] w = splitWhitespace(before); for (int i = 0; i < w.length; i++) { add(w[i], t); } } add(quoted, t); quote = cmd.indexOf('"'); } else { String[] w = splitWhitespace(cmd); for (int i = 0; i < w.length; i++) { add(w[i], t); } cmd = ""; } } } else { String[] w = splitWhitespace(cmd); for (int i = 0; i < w.length; i++) { add(w[i], t); } } tokens = (String[])t.toArray(new String[0]); i = 0; length = tokens.length; //for (int i = 0; i < tokens.length; i++) { // System.out.println("\"" + tokens[i] + "\""); //} } String nextToken() { return tokens[i++]; } boolean hasMoreTokens() { return i < length; } int countTokens() { return length - i; } void trim(int n) { if (length >= n) { length -= n; } else { throw new IndexOutOfBoundsException(String.valueOf(n)); } } String join(String sep) { StringBuffer result = new StringBuffer(); for (int w = i; w < length; w++) { result.append(tokens[w]); if (w + 1 < length) { result.append(sep); } } return result.toString(); } String at(int i) { if (i < 0 || i >= length) { throw new IndexOutOfBoundsException(String.valueOf(i)); } return tokens[i]; } } abstract class Command { Command(String n, String u, boolean ok) { name = n; usage = u; okIfDisconnected = ok; } Command(String n, boolean ok) { name = n; usage = n; okIfDisconnected = ok; } final String name; final String usage; final boolean okIfDisconnected; abstract void doit(Tokens t); void usage() { out.println("Usage: " + usage); } void printOopValue(Oop oop) { if (oop != null) { Klass k = oop.getKlass(); Symbol s = k.getName(); if (s != null) { out.print("Oop for " + s.asString() + " @ "); } else { out.print("Oop @ "); } Oop.printOopAddressOn(oop, out); } else { out.print("null"); } } void printNode(SimpleTreeNode node) { int count = node.getChildCount(); for (int i = 0; i < count; i++) { try { SimpleTreeNode field = node.getChild(i); if (field instanceof OopTreeNodeAdapter) { out.print(field); out.print(" "); printOopValue(((OopTreeNodeAdapter)field).getOop()); out.println(); } else { out.println(field); } } catch (Exception e) { out.println(); out.println("Error: " + e); if (verboseExceptions) { e.printStackTrace(out); } } } } } void quote(String s) { if (s.indexOf(" ") == -1) { out.print(s); } else { out.print("\""); out.print(s); out.print("\""); } } void dumpType(Type type) { out.print("type "); quote(type.getName()); out.print(" "); if (type.getSuperclass() != null) { quote(type.getSuperclass().getName()); out.print(" "); } else { out.print("null "); } out.print(type.isOopType()); out.print(" "); if (type.isCIntegerType()) { out.print("true "); out.print(((CIntegerType)type).isUnsigned()); out.print(" "); } else { out.print("false false "); } out.print(type.getSize()); out.println(); } void dumpFields(Type type) { dumpFields(type, true); } void dumpFields(Type type, boolean allowStatic) { Iterator i = type.getFields(); while (i.hasNext()) { Field f = (Field) i.next(); if (!allowStatic && f.isStatic()) continue; out.print("field "); quote(type.getName()); out.print(" "); out.print(f.getName()); out.print(" "); quote(f.getType().getName()); out.print(" "); out.print(f.isStatic()); out.print(" "); if (f.isStatic()) { out.print("0 "); out.print(f.getStaticFieldAddress()); } else { out.print(f.getOffset()); out.print(" 0x0"); } out.println(); } } Address lookup(String symbol) { if (symbol.indexOf("::") != -1) { String[] parts = symbol.split("::"); StringBuffer mangled = new StringBuffer("__1c"); for (int i = 0; i < parts.length; i++) { int len = parts[i].length(); if (len >= 26) { mangled.append((char)('a' + (len / 26))); len = len % 26; } mangled.append((char)('A' + len)); mangled.append(parts[i]); } mangled.append("_"); symbol = mangled.toString(); } return VM.getVM().getDebugger().lookup(null, symbol); } Address parseAddress(String addr) { return VM.getVM().getDebugger().parseAddress(addr); } private final Command[] commandList = { new Command("reattach", true) { public void doit(Tokens t) { int tokens = t.countTokens(); if (tokens != 0) { usage(); return; } preAttach(); debugger.reattach(); postAttach(); } }, new Command("attach", "attach pid | exec core", true) { public void doit(Tokens t) { int tokens = t.countTokens(); if (tokens == 1) { preAttach(); debugger.attach(t.nextToken()); postAttach(); } else if (tokens == 2) { preAttach(); debugger.attach(t.nextToken(), t.nextToken()); postAttach(); } else { usage(); } } }, new Command("detach", false) { public void doit(Tokens t) { if (t.countTokens() != 0) { usage(); } else { debugger.detach(); } } }, new Command("examine", "examine [ address/count ] | [ address,address]", false) { Pattern args1 = Pattern.compile("^(0x[0-9a-f]+)(/([0-9]*)([a-z]*))?$"); Pattern args2 = Pattern.compile("^(0x[0-9a-f]+),(0x[0-9a-f]+)(/[a-z]*)?$"); String fill(Address a, int width) { String s = "0x0"; if (a != null) { s = a.toString(); } if (s.length() != width) { return s.substring(0, 2) + "000000000000000000000".substring(0, width - s.length()) + s.substring(2); } return s; } public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { String arg = t.nextToken(); Matcher m1 = args1.matcher(arg); Matcher m2 = args2.matcher(arg); Address start = null; Address end = null; String format = ""; int formatSize = (int)VM.getVM().getAddressSize(); if (m1.matches()) { start = VM.getVM().getDebugger().parseAddress(m1.group(1)); int count = 1; if (m1.group(2) != null) { count = Integer.parseInt(m1.group(3)); } end = start.addOffsetTo(count * formatSize); } else if (m2.matches()) { start = VM.getVM().getDebugger().parseAddress(m2.group(1)); end = VM.getVM().getDebugger().parseAddress(m2.group(2)); } else { usage(); return; } int line = 80; int formatWidth = formatSize * 8 / 4 + 2; out.print(fill(start, formatWidth)); out.print(": "); int width = line - formatWidth - 2; boolean needsPrintln = true; while (start != null && start.lessThan(end)) { Address val = start.getAddressAt(0); out.print(fill(val, formatWidth)); needsPrintln = true; width -= formatWidth; start = start.addOffsetTo(formatSize); if (width <= formatWidth) { out.println(); needsPrintln = false; if (start.lessThan(end)) { out.print(fill(start, formatWidth)); out.print(": "); width = line - formatWidth - 2; } } else { out.print(" "); width -= 1; } } if (needsPrintln) { out.println(); } } } }, new Command("dumpreplaydata", "dumpreplaydata { <address > | -a | <thread_id> }", false) { // This is used to dump replay data from ciInstanceKlass, ciMethodData etc // default file name is replay.txt, also if java crashes in compiler // thread, this file will be dumped in error processing. public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); return; } String name = t.nextToken(); Address a = null; try { a = VM.getVM().getDebugger().parseAddress(name); } catch (NumberFormatException e) { } if (a != null) { // only nmethod, Method, MethodData and InstanceKlass needed to // dump replay data CodeBlob cb = VM.getVM().getCodeCache().findBlob(a); if (cb != null && (cb instanceof NMethod)) { ((NMethod)cb).dumpReplayData(out); return; } // assume it is Metadata Metadata meta = Metadata.instantiateWrapperFor(a); if (meta != null) { meta.dumpReplayData(out); } else { usage(); return; } } // Not an address boolean all = name.equals("-a"); Threads threads = VM.getVM().getThreads(); for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); if (all || bos.toString().equals(name)) { if (thread instanceof CompilerThread) { CompilerThread ct = (CompilerThread)thread; ciEnv env = ct.env(); if (env != null) { env.dumpReplayData(out); } } } } } }, new Command("buildreplayjars", "buildreplayjars [ all | app | boot ] | [ prefix ]", false) { // This is used to dump jar files of all the classes // loaded in the core. Everything with null classloader // will go in boot.jar and everything else will go in // app.jar. boot.jar usually not needed, unless changed by jvmti. public void doit(Tokens t) { int tcount = t.countTokens(); if (tcount > 2) { usage(); return; } try { String prefix = ""; String option = "all"; // default switch(tcount) { case 0: break; case 1: option = t.nextToken(); if (!option.equalsIgnoreCase("all") && !option.equalsIgnoreCase("app") && !option.equalsIgnoreCase("root")) { prefix = option; option = "all"; } break; case 2: option = t.nextToken(); prefix = t.nextToken(); break; default: usage(); return; } if (!option.equalsIgnoreCase("all") && !option.equalsIgnoreCase("app") && !option.equalsIgnoreCase("boot")) { usage(); return; } ClassDump cd = new ClassDump(); if (option.equalsIgnoreCase("all") || option.equalsIgnoreCase("boot")) { cd.setClassFilter(new BootFilter()); cd.setJarOutput(prefix + "boot.jar"); cd.run(); } if (option.equalsIgnoreCase("all") || option.equalsIgnoreCase("app")) { cd.setClassFilter(new NonBootFilter()); cd.setJarOutput(prefix + "app.jar"); cd.run(); } } catch (IOException ioe) { ioe.printStackTrace(); } } }, new Command("findpc", "findpc address", false) { public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); PointerLocation loc = PointerFinder.find(a); loc.printOn(out); } } }, new Command("symbol", "symbol address", false) { public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); Symbol.create(a).printValueOn(out); out.println(); } } }, new Command("symboltable", "symboltable name", false) { public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { out.println(SymbolTable.getTheTable().probe(t.nextToken())); } } }, new Command("symboldump", "symboldump", false) { public void doit(Tokens t) { SymbolTable.getTheTable().symbolsDo(new SymbolTable.SymbolVisitor() { public void visit(Symbol sym) { sym.printValueOn(out); out.println(); } }); } }, new Command("flags", "flags [ flag | -nd ]", false) { public void doit(Tokens t) { int tokens = t.countTokens(); if (tokens != 0 && tokens != 1) { usage(); } else { String name = tokens > 0 ? t.nextToken() : null; boolean nonDefault = false; if (name != null && name.equals("-nd")) { name = null; nonDefault = true; } VM.Flag[] flags = VM.getVM().getCommandLineFlags(); if (flags == null) { out.println("Command Flag info not available (use 1.4.1_03 or later)!"); } else { boolean printed = false; for (int f = 0; f < flags.length; f++) { VM.Flag flag = flags[f]; if (name == null || flag.getName().equals(name)) { if (nonDefault && flag.getOrigin() == 0) { // only print flags which aren't their defaults continue; } out.println(flag.getName() + " = " + flag.getValue() + " " + flag.getOrigin()); printed = true; } } if (name != null && !printed) { out.println("Couldn't find flag: " + name); } } } } }, new Command("help", "help [ command ]", true) { public void doit(Tokens t) { int tokens = t.countTokens(); Command cmd = null; if (tokens == 1) { cmd = findCommand(t.nextToken()); } if (cmd != null) { cmd.usage(); } else if (tokens == 0) { out.println("Available commands:"); Object[] keys = commands.keySet().toArray(); Arrays.sort(keys, new Comparator() { public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); } }); for (int i = 0; i < keys.length; i++) { out.print(" "); out.println(((Command)commands.get(keys[i])).usage); } } } }, new Command("history", "history", true) { public void doit(Tokens t) { int tokens = t.countTokens(); if (tokens != 0 && (tokens != 1 || !t.nextToken().equals("-h"))) { usage(); return; } boolean printIndex = tokens == 0; for (int i = 0; i < history.size(); i++) { if (printIndex) out.print(i + " "); out.println(history.get(i)); } } }, // decode raw address new Command("dis", "dis address [length]", false) { public void doit(Tokens t) { int tokens = t.countTokens(); if (tokens != 1 && tokens != 2) { usage(); return; } String name = t.nextToken(); Address addr = null; int len = 0x10; // default length try { addr = VM.getVM().getDebugger().parseAddress(name); } catch (NumberFormatException e) { out.println(e); return; } if (tokens == 2) { try { len = Integer.parseInt(t.nextToken()); } catch (NumberFormatException e) { out.println(e); return; } } HTMLGenerator generator = new HTMLGenerator(false); out.println(generator.genHTMLForRawDisassembly(addr, len)); } }, // decode codeblob or nmethod new Command("disassemble", "disassemble address", false) { public void doit(Tokens t) { int tokens = t.countTokens(); if (tokens != 1) { usage(); return; } String name = t.nextToken(); Address addr = null; try { addr = VM.getVM().getDebugger().parseAddress(name); } catch (NumberFormatException e) { out.println(e); return; } HTMLGenerator generator = new HTMLGenerator(false); out.println(generator.genHTML(addr)); } }, // print Java bytecode disassembly new Command("jdis", "jdis address", false) { public void doit(Tokens t) { int tokens = t.countTokens(); if (tokens != 1) { usage(); return; } Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); Method m = (Method)Metadata.instantiateWrapperFor(a); HTMLGenerator html = new HTMLGenerator(false); out.println(html.genHTML(m)); } }, new Command("revptrs", "revptrs address", false) { public void doit(Tokens t) { int tokens = t.countTokens(); if (tokens != 1 && (tokens != 2 || !t.nextToken().equals("-c"))) { usage(); return; } boolean chase = tokens == 2; ReversePtrs revptrs = VM.getVM().getRevPtrs(); if (revptrs == null) { out.println("Computing reverse pointers..."); ReversePtrsAnalysis analysis = new ReversePtrsAnalysis(); final boolean[] complete = new boolean[1]; HeapProgressThunk thunk = new HeapProgressThunk() { public void heapIterationFractionUpdate(double d) {} public synchronized void heapIterationComplete() { complete[0] = true; notify(); } }; analysis.setHeapProgressThunk(thunk); analysis.run(); while (!complete[0]) { synchronized (thunk) { try { thunk.wait(); } catch (Exception e) { } } } revptrs = VM.getVM().getRevPtrs(); out.println("Done."); } Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); if (VM.getVM().getUniverse().heap().isInReserved(a)) { OopHandle handle = a.addOffsetToAsOopHandle(0); Oop oop = VM.getVM().getObjectHeap().newOop(handle); ArrayList ptrs = revptrs.get(oop); if (ptrs == null) { out.println("no live references to " + a); } else { if (chase) { while (ptrs.size() == 1) { LivenessPathElement e = (LivenessPathElement)ptrs.get(0); ByteArrayOutputStream bos = new ByteArrayOutputStream(); Oop.printOopValueOn(e.getObj(), new PrintStream(bos)); out.println(bos.toString()); ptrs = revptrs.get(e.getObj()); } } else { for (int i = 0; i < ptrs.size(); i++) { LivenessPathElement e = (LivenessPathElement)ptrs.get(i); ByteArrayOutputStream bos = new ByteArrayOutputStream(); Oop.printOopValueOn(e.getObj(), new PrintStream(bos)); out.println(bos.toString()); oop = e.getObj(); } } } } } }, new Command("printmdo", "printmdo [ -a | expression ]", false) { // Print every MDO in the heap or the one referenced by expression. public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { String s = t.nextToken(); if (s.equals("-a")) { SystemDictionary sysDict = VM.getVM().getSystemDictionary(); sysDict.allClassesDo(new SystemDictionary.ClassVisitor() { public void visit(Klass k) { if (k instanceof InstanceKlass) { MethodArray methods = ((InstanceKlass)k).getMethods(); for (int i = 0; i < methods.length(); i++) { Method m = methods.at(i); MethodData mdo = m.getMethodData(); if (mdo != null) { out.println("MethodData " + mdo.getAddress() + " for " + "method " + m.getMethodHolder().getName().asString() + "." + m.getName().asString() + m.getSignature().asString() + "@" + m.getAddress()); mdo.printDataOn(out); } } } } } ); } else { Address a = VM.getVM().getDebugger().parseAddress(s); MethodData mdo = (MethodData) Metadata.instantiateWrapperFor(a); mdo.printDataOn(out); } } } }, new Command("printall", "printall", false) { // Print every MDO in the heap or the one referenced by expression. public void doit(Tokens t) { if (t.countTokens() != 0) { usage(); } else { SystemDictionary sysDict = VM.getVM().getSystemDictionary(); sysDict.allClassesDo(new SystemDictionary.ClassVisitor() { public void visit(Klass k) { if (k instanceof InstanceKlass && ((InstanceKlass)k).getConstants().getCache() != null) { MethodArray methods = ((InstanceKlass)k).getMethods(); for (int i = 0; i < methods.length(); i++) { Method m = methods.at(i); HTMLGenerator gen = new HTMLGenerator(false); out.println(gen.genHTML(m)); } } } } ); } } }, new Command("dumpideal", "dumpideal { -a | id }", false) { // Do a full dump of the nodes reachabile from root in each compiler thread. public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { String name = t.nextToken(); boolean all = name.equals("-a"); Threads threads = VM.getVM().getThreads(); for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); if (all || bos.toString().equals(name)) { if (thread instanceof CompilerThread) { CompilerThread ct = (CompilerThread)thread; out.println(ct); ciEnv env = ct.env(); if (env != null) { Compile c = env.compilerData(); c.root().dump(9999, out); } else { out.println(" not compiling"); } } } } } } }, new Command("dumpcfg", "dumpcfg { -a | id }", false) { // Dump the PhaseCFG for every compiler thread that has one live. public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { String name = t.nextToken(); boolean all = name.equals("-a"); Threads threads = VM.getVM().getThreads(); for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); if (all || bos.toString().equals(name)) { if (thread instanceof CompilerThread) { CompilerThread ct = (CompilerThread)thread; out.println(ct); ciEnv env = ct.env(); if (env != null) { Compile c = env.compilerData(); c.cfg().dump(out); } } } } } } }, new Command("dumpilt", "dumpilt { -a | id }", false) { // dumps the InlineTree of a C2 compile public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { String name = t.nextToken(); boolean all = name.equals("-a"); Threads threads = VM.getVM().getThreads(); for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); if (all || bos.toString().equals(name)) { if (thread instanceof CompilerThread) { CompilerThread ct = (CompilerThread)thread; ciEnv env = ct.env(); if (env != null) { Compile c = env.compilerData(); InlineTree ilt = c.ilt(); if (ilt != null) { ilt.print(out); } } } } } } } }, new Command("vmstructsdump", "vmstructsdump", false) { public void doit(Tokens t) { if (t.countTokens() != 0) { usage(); return; } // Dump a copy of the type database in a form that can // be read back. Iterator i = agent.getTypeDataBase().getTypes(); // Make sure the types are emitted in an order than can be read back in HashSet emitted = new HashSet(); Stack pending = new Stack(); while (i.hasNext()) { Type n = (Type)i.next(); if (emitted.contains(n.getName())) { continue; } while (n != null && !emitted.contains(n.getName())) { pending.push(n); n = n.getSuperclass(); } while (!pending.empty()) { n = (Type)pending.pop(); dumpType(n); emitted.add(n.getName()); } } i = agent.getTypeDataBase().getTypes(); while (i.hasNext()) { dumpFields((Type)i.next(), false); } } }, new Command("inspect", "inspect expression", false) { public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); SimpleTreeNode node = null; if (VM.getVM().getUniverse().heap().isInReserved(a)) { OopHandle handle = a.addOffsetToAsOopHandle(0); Oop oop = VM.getVM().getObjectHeap().newOop(handle); node = new OopTreeNodeAdapter(oop, null); out.println("instance of " + node.getValue() + " @ " + a + " (size = " + oop.getObjectSize() + ")"); } else if (VM.getVM().getCodeCache().contains(a)) { CodeBlob blob = VM.getVM().getCodeCache().findBlobUnsafe(a); a = blob.headerBegin(); } if (node == null) { Type type = VM.getVM().getTypeDataBase().guessTypeForAddress(a); if (type != null) { out.println("Type is " + type.getName() + " (size of " + type.getSize() + ")"); node = new CTypeTreeNodeAdapter(a, type, null); } } if (node != null) { printNode(node); } } } }, new Command("jhisto", "jhisto", false) { public void doit(Tokens t) { ObjectHistogram histo = new ObjectHistogram(); histo.run(out, err); } }, new Command("jstack", "jstack [-v]", false) { public void doit(Tokens t) { boolean verbose = false; if (t.countTokens() > 0 && t.nextToken().equals("-v")) { verbose = true; } StackTrace jstack = new StackTrace(verbose, true); jstack.run(out); } }, new Command("print", "print expression", false) { public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); HTMLGenerator gen = new HTMLGenerator(false); out.println(gen.genHTML(a)); } } }, new Command("printas", "printas type expression", false) { public void doit(Tokens t) { if (t.countTokens() != 2) { usage(); } else { Type type = agent.getTypeDataBase().lookupType(t.nextToken()); Address a = VM.getVM().getDebugger().parseAddress(t.nextToken()); CTypeTreeNodeAdapter node = new CTypeTreeNodeAdapter(a, type, null); out.println("pointer to " + type + " @ " + a + " (size = " + type.getSize() + ")"); printNode(node); } } }, new Command("printstatics", "printstatics [ type ]", false) { public void doit(Tokens t) { if (t.countTokens() > 1) { usage(); } else { if (t.countTokens() == 0) { out.println("All known static fields"); printNode(new CTypeTreeNodeAdapter(agent.getTypeDataBase().getTypes())); } else { Type type = agent.getTypeDataBase().lookupType(t.nextToken()); out.println("Static fields of " + type.getName()); printNode(new CTypeTreeNodeAdapter(type)); } } } }, new Command("pmap", "pmap", false) { public void doit(Tokens t) { PMap pmap = new PMap(); pmap.run(out, debugger.getAgent().getDebugger()); } }, new Command("pstack", "pstack [-v]", false) { public void doit(Tokens t) { boolean verbose = false; if (t.countTokens() > 0 && t.nextToken().equals("-v")) { verbose = true; } PStack pstack = new PStack(verbose, true); pstack.run(out, debugger.getAgent().getDebugger()); } }, new Command("quit", true) { public void doit(Tokens t) { if (t.countTokens() != 0) { usage(); } else { debugger.detach(); quit = true; } } }, new Command("echo", "echo [ true | false ]", true) { public void doit(Tokens t) { if (t.countTokens() == 0) { out.println("echo is " + doEcho); } else if (t.countTokens() == 1) { doEcho = Boolean.valueOf(t.nextToken()).booleanValue(); } else { usage(); } } }, new Command("versioncheck", "versioncheck [ true | false ]", true) { public void doit(Tokens t) { if (t.countTokens() == 0) { out.println("versioncheck is " + (System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null)); } else if (t.countTokens() == 1) { if (Boolean.valueOf(t.nextToken()).booleanValue()) { System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", null); } else { System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", "true"); } } else { usage(); } } }, new Command("scanoops", "scanoops start end [ type ]", false) { public void doit(Tokens t) { if (t.countTokens() != 2 && t.countTokens() != 3) { usage(); } else { long stride = VM.getVM().getAddressSize(); Address base = VM.getVM().getDebugger().parseAddress(t.nextToken()); Address end = VM.getVM().getDebugger().parseAddress(t.nextToken()); Klass klass = null; if (t.countTokens() == 1) { klass = SystemDictionaryHelper.findInstanceKlass(t.nextToken()); if (klass == null) { out.println("No such type."); return; } } while (base != null && base.lessThan(end)) { long step = stride; OopHandle handle = base.addOffsetToAsOopHandle(0); if (RobustOopDeterminator.oopLooksValid(handle)) { try { Oop oop = VM.getVM().getObjectHeap().newOop(handle); if (klass == null || oop.getKlass().isSubtypeOf(klass)) out.println(handle.toString() + " " + oop.getKlass().getName().asString()); step = oop.getObjectSize(); } catch (UnknownOopException ex) { // ok } catch (RuntimeException ex) { ex.printStackTrace(); } } base = base.addOffsetTo(step); } } } }, new Command("intConstant", "intConstant [ name [ value ] ]", true) { public void doit(Tokens t) { if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 2) { usage(); return; } HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase(); if (t.countTokens() == 1) { String name = t.nextToken(); out.println("intConstant " + name + " " + db.lookupIntConstant(name)); } else if (t.countTokens() == 0) { Iterator i = db.getIntConstants(); while (i.hasNext()) { String name = (String)i.next(); out.println("intConstant " + name + " " + db.lookupIntConstant(name)); } } else if (t.countTokens() == 2) { String name = t.nextToken(); Integer value = Integer.valueOf(t.nextToken()); db.addIntConstant(name, value); } } }, new Command("longConstant", "longConstant [ name [ value ] ]", true) { public void doit(Tokens t) { if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 2) { usage(); return; } HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase(); if (t.countTokens() == 1) { String name = t.nextToken(); out.println("longConstant " + name + " " + db.lookupLongConstant(name)); } else if (t.countTokens() == 0) { Iterator i = db.getLongConstants(); while (i.hasNext()) { String name = (String)i.next(); out.println("longConstant " + name + " " + db.lookupLongConstant(name)); } } else if (t.countTokens() == 2) { String name = t.nextToken(); Long value = Long.valueOf(t.nextToken()); db.addLongConstant(name, value); } } }, new Command("field", "field [ type [ name fieldtype isStatic offset address ] ]", true) { public void doit(Tokens t) { if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) { usage(); return; } if (t.countTokens() == 1) { Type type = agent.getTypeDataBase().lookupType(t.nextToken()); dumpFields(type); } else if (t.countTokens() == 0) { Iterator i = agent.getTypeDataBase().getTypes(); while (i.hasNext()) { dumpFields((Type)i.next()); } } else { BasicType containingType = (BasicType)agent.getTypeDataBase().lookupType(t.nextToken()); String fieldName = t.nextToken(); // The field's Type must already be in the database -- no exceptions Type fieldType = agent.getTypeDataBase().lookupType(t.nextToken()); boolean isStatic = Boolean.valueOf(t.nextToken()).booleanValue(); long offset = Long.parseLong(t.nextToken()); Address staticAddress = parseAddress(t.nextToken()); if (isStatic && staticAddress == null) { staticAddress = lookup(containingType.getName() + "::" + fieldName); } // check to see if the field already exists Iterator i = containingType.getFields(); while (i.hasNext()) { Field f = (Field) i.next(); if (f.getName().equals(fieldName)) { if (f.isStatic() != isStatic) { throw new RuntimeException("static/nonstatic mismatch: " + t.input); } if (!isStatic) { if (f.getOffset() != offset) { throw new RuntimeException("bad redefinition of field offset: " + t.input); } } else { if (!f.getStaticFieldAddress().equals(staticAddress)) { throw new RuntimeException("bad redefinition of field location: " + t.input); } } if (f.getType() != fieldType) { throw new RuntimeException("bad redefinition of field type: " + t.input); } return; } } // Create field by type HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase(); db.createField(containingType, fieldName, fieldType, isStatic, offset, staticAddress); } } }, new Command("tokenize", "tokenize ...", true) { public void doit(Tokens t) { while (t.hasMoreTokens()) { out.println("\"" + t.nextToken() + "\""); } } }, new Command("type", "type [ type [ name super isOop isInteger isUnsigned size ] ]", true) { public void doit(Tokens t) { if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) { usage(); return; } if (t.countTokens() == 6) { String typeName = t.nextToken(); String superclassName = t.nextToken(); if (superclassName.equals("null")) { superclassName = null; } boolean isOop = Boolean.valueOf(t.nextToken()).booleanValue(); boolean isInteger = Boolean.valueOf(t.nextToken()).booleanValue(); boolean isUnsigned = Boolean.valueOf(t.nextToken()).booleanValue(); long size = Long.parseLong(t.nextToken()); BasicType type = null; try { type = (BasicType)agent.getTypeDataBase().lookupType(typeName); } catch (RuntimeException e) { } if (type != null) { if (type.isOopType() != isOop) { throw new RuntimeException("oop mismatch in type definition: " + t.input); } if (type.isCIntegerType() != isInteger) { throw new RuntimeException("integer type mismatch in type definition: " + t.input); } if (type.isCIntegerType() && (((CIntegerType)type).isUnsigned()) != isUnsigned) { throw new RuntimeException("unsigned mismatch in type definition: " + t.input); } if (type.getSuperclass() == null) { if (superclassName != null) { if (type.getSize() == -1) { type.setSuperclass(agent.getTypeDataBase().lookupType(superclassName)); } else { throw new RuntimeException("unexpected superclass in type definition: " + t.input); } } } else { if (superclassName == null) { throw new RuntimeException("missing superclass in type definition: " + t.input); } if (!type.getSuperclass().getName().equals(superclassName)) { throw new RuntimeException("incorrect superclass in type definition: " + t.input); } } if (type.getSize() != size) { if (type.getSize() == -1) { type.setSize(size); } throw new RuntimeException("size mismatch in type definition: " + t.input); } return; } // Create type HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase(); db.createType(typeName, superclassName, isOop, isInteger, isUnsigned, size); } else if (t.countTokens() == 1) { Type type = agent.getTypeDataBase().lookupType(t.nextToken()); dumpType(type); } else { Iterator i = agent.getTypeDataBase().getTypes(); // Make sure the types are emitted in an order than can be read back in HashSet emitted = new HashSet(); Stack pending = new Stack(); while (i.hasNext()) { Type n = (Type)i.next(); if (emitted.contains(n.getName())) { continue; } while (n != null && !emitted.contains(n.getName())) { pending.push(n); n = n.getSuperclass(); } while (!pending.empty()) { n = (Type)pending.pop(); dumpType(n); emitted.add(n.getName()); } } } } }, new Command("source", "source filename", true) { public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); return; } String file = t.nextToken(); BufferedReader savedInput = in; try { BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(file))); in = input; run(false); } catch (Exception e) { out.println("Error: " + e); if (verboseExceptions) { e.printStackTrace(out); } } finally { in = savedInput; } } }, new Command("search", "search [ heap | perm | rawheap | codecache | threads ] value", false) { public void doit(Tokens t) { if (t.countTokens() != 2) { usage(); return; } String type = t.nextToken(); final Address value = VM.getVM().getDebugger().parseAddress(t.nextToken()); final long stride = VM.getVM().getAddressSize(); if (type.equals("threads")) { Threads threads = VM.getVM().getThreads(); for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { Address base = thread.getStackBase(); Address end = thread.getLastJavaSP(); if (end == null) continue; if (end.lessThan(base)) { Address tmp = base; base = end; end = tmp; } //out.println("Searching " + base + " " + end); while (base != null && base.lessThan(end)) { Address val = base.getAddressAt(0); if (AddressOps.equal(val, value)) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); out.println("found on the stack of thread " + bos.toString() + " at " + base); } base = base.addOffsetTo(stride); } } } else if (type.equals("rawheap")) { RawHeapVisitor iterator = new RawHeapVisitor() { public void prologue(long used) { } public void visitAddress(Address addr) { Address val = addr.getAddressAt(0); if (AddressOps.equal(val, value)) { out.println("found at " + addr); } } public void visitCompOopAddress(Address addr) { Address val = addr.getCompOopAddressAt(0); if (AddressOps.equal(val, value)) { out.println("found at " + addr); } } public void epilogue() { } }; VM.getVM().getObjectHeap().iterateRaw(iterator); } else if (type.equals("heap")) { HeapVisitor iterator = new DefaultHeapVisitor() { public boolean doObj(Oop obj) { int index = 0; Address start = obj.getHandle(); long end = obj.getObjectSize(); while (index < end) { Address val = start.getAddressAt(index); if (AddressOps.equal(val, value)) { out.println("found in " + obj.getHandle()); break; } index += 4; } return false; } }; VM.getVM().getObjectHeap().iterate(iterator); } else if (type.equals("codecache")) { CodeCacheVisitor v = new CodeCacheVisitor() { public void prologue(Address start, Address end) { } public void visit(CodeBlob blob) { boolean printed = false; Address base = blob.getAddress(); Address end = base.addOffsetTo(blob.getSize()); while (base != null && base.lessThan(end)) { Address val = base.getAddressAt(0); if (AddressOps.equal(val, value)) { if (!printed) { printed = true; try { blob.printOn(out); } catch (Exception e) { out.println("Exception printing blob at " + base); e.printStackTrace(); } } out.println("found at " + base + "\n"); } base = base.addOffsetTo(stride); } } public void epilogue() { } }; VM.getVM().getCodeCache().iterate(v); } } }, new Command("dumpcodecache", "dumpcodecache", false) { public void doit(Tokens t) { if (t.countTokens() != 0) { usage(); } else { final PrintStream fout = out; final HTMLGenerator gen = new HTMLGenerator(false); CodeCacheVisitor v = new CodeCacheVisitor() { public void prologue(Address start, Address end) { } public void visit(CodeBlob blob) { fout.println(gen.genHTML(blob.contentBegin())); } public void epilogue() { } }; VM.getVM().getCodeCache().iterate(v); } } }, new Command("where", "where { -a | id }", false) { public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { String name = t.nextToken(); Threads threads = VM.getVM().getThreads(); boolean all = name.equals("-a"); for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); if (all || bos.toString().equals(name)) { out.println("Thread " + bos.toString() + " Address: " + thread.getAddress()); HTMLGenerator gen = new HTMLGenerator(false); try { out.println(gen.genHTMLForJavaStackTrace(thread)); } catch (Exception e) { err.println("Error: " + e); if (verboseExceptions) { e.printStackTrace(err); } } if (!all) return; } } if (!all) out.println("Couldn't find thread " + name); } } }, new Command("thread", "thread { -a | id }", false) { public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { String name = t.nextToken(); Threads threads = VM.getVM().getThreads(); boolean all = name.equals("-a"); for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); if (all || bos.toString().equals(name)) { out.println("Thread " + bos.toString() + " Address " + thread.getAddress()); thread.printInfoOn(out); out.println(" "); if (!all) return; } } out.println("Couldn't find thread " + name); } } }, new Command("threads", false) { public void doit(Tokens t) { if (t.countTokens() != 0) { usage(); } else { Threads threads = VM.getVM().getThreads(); for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { thread.printThreadIDOn(out); out.println(" " + thread.getThreadName()); thread.printInfoOn(out); out.println("\n..."); } } } }, new Command("livenmethods", false) { public void doit(Tokens t) { if (t.countTokens() != 0) { usage(); } else { ArrayList nmethods = new ArrayList(); Threads threads = VM.getVM().getThreads(); HTMLGenerator gen = new HTMLGenerator(false); for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { try { for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) { if (vf instanceof CompiledVFrame) { NMethod c = ((CompiledVFrame)vf).getCode(); if (!nmethods.contains(c)) { nmethods.add(c); out.println(gen.genHTML(c)); } } } } catch (Exception e) { e.printStackTrace(); } } } } }, new Command("universe", false) { public void doit(Tokens t) { if (t.countTokens() != 0) { usage(); } else { Universe u = VM.getVM().getUniverse(); out.println("Heap Parameters:"); u.heap().printOn(out); } } }, new Command("verbose", "verbose true | false", true) { public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { verboseExceptions = Boolean.valueOf(t.nextToken()).booleanValue(); } } }, new Command("assert", "assert true | false", true) { public void doit(Tokens t) { if (t.countTokens() != 1) { usage(); } else { Assert.ASSERTS_ENABLED = Boolean.valueOf(t.nextToken()).booleanValue(); } } }, }; private boolean verboseExceptions = false; private ArrayList history = new ArrayList(); private HashMap commands = new HashMap(); private boolean doEcho = false; private Command findCommand(String key) { return (Command)commands.get(key); } public void printPrompt() { out.print("hsdb> "); } private DebuggerInterface debugger; private HotSpotAgent agent; private JSJavaScriptEngine jsengine; private BufferedReader in; private PrintStream out; private PrintStream err; // called before debuggee attach private void preAttach() { // nothing for now.. } // called after debuggee attach private void postAttach() { // create JavaScript engine and start it try { jsengine = new JSJavaScriptEngine() { private ObjectReader reader = new ObjectReader(); private JSJavaFactory factory = new JSJavaFactoryImpl(); public ObjectReader getObjectReader() { return reader; } public JSJavaFactory getJSJavaFactory() { return factory; } protected void quit() { debugger.detach(); quit = true; } protected BufferedReader getInputReader() { return in; } protected PrintStream getOutputStream() { return out; } protected PrintStream getErrorStream() { return err; } }; try { jsengine.defineFunction(this, this.getClass().getMethod("registerCommand", new Class[] { String.class, String.class, String.class })); } catch (NoSuchMethodException exp) { // should not happen, see below...!! exp.printStackTrace(); } jsengine.start(); } catch (Exception ex) { System.out.println("Warning! JS Engine can't start, some commands will not be available."); if (verboseExceptions) { ex.printStackTrace(out); } } } public void registerCommand(String cmd, String usage, final String func) { commands.put(cmd, new Command(cmd, usage, false) { public void doit(Tokens t) { final int len = t.countTokens(); Object[] args = new Object[len]; for (int i = 0; i < len; i++) { args[i] = t.nextToken(); } jsengine.call(func, args); } }); } public void setOutput(PrintStream o) { out = o; } public void setErr(PrintStream e) { err = e; } public CommandProcessor(DebuggerInterface debugger, BufferedReader in, PrintStream out, PrintStream err) { this.debugger = debugger; this.agent = debugger.getAgent(); this.in = in; this.out = out; this.err = err; for (int i = 0; i < commandList.length; i++) { Command c = commandList[i]; if (commands.get(c.name) != null) { throw new InternalError(c.name + " has multiple definitions"); } commands.put(c.name, c); } if (debugger.isAttached()) { postAttach(); } } public void run(boolean prompt) { // Process interactive commands. while (!quit) { if (prompt) printPrompt(); String ln = null; try { ln = in.readLine(); } catch (IOException e) { } if (ln == null) { if (prompt) err.println("Input stream closed."); return; } executeCommand(ln, prompt); } } static Pattern historyPattern = Pattern.compile("((!\\*)|(!\\$)|(!!-?)|(!-?[0-9][0-9]*)|(![a-zA-Z][^ ]*))"); public void executeCommand(String ln, boolean putInHistory) { if (ln.indexOf('!') != -1) { int size = history.size(); if (size == 0) { ln = ""; err.println("History is empty"); } else { StringBuffer result = new StringBuffer(); Matcher m = historyPattern.matcher(ln); int start = 0; while (m.find()) { if (m.start() > start) { result.append(ln.substring(start, m.start() - start)); } start = m.end(); String cmd = m.group(); if (cmd.equals("!!")) { result.append((String)history.get(history.size() - 1)); } else if (cmd.equals("!!-")) { Tokens item = new Tokens((String)history.get(history.size() - 1)); item.trim(1); result.append(item.join(" ")); } else if (cmd.equals("!*")) { Tokens item = new Tokens((String)history.get(history.size() - 1)); item.nextToken(); result.append(item.join(" ")); } else if (cmd.equals("!$")) { Tokens item = new Tokens((String)history.get(history.size() - 1)); result.append(item.at(item.countTokens() - 1)); } else { String tail = cmd.substring(1); switch (tail.charAt(0)) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': { int index = Integer.parseInt(tail); if (index < 0) { index = history.size() + index; } if (index > size) { err.println("No such history item"); } else { result.append((String)history.get(index)); } break; } default: { for (int i = history.size() - 1; i >= 0; i--) { String s = (String)history.get(i); if (s.startsWith(tail)) { result.append(s); } } } } } } if (result.length() == 0) { err.println("malformed history reference"); ln = ""; } else { if (start < ln.length()) { result.append(ln.substring(start)); } ln = result.toString(); if (!doEcho) { out.println(ln); } } } } if (doEcho) { out.println("+ " + ln); } PrintStream redirect = null; Tokens t = new Tokens(ln); if (t.hasMoreTokens()) { boolean error = false; if (putInHistory) history.add(ln); int len = t.countTokens(); if (len > 2) { String r = t.at(len - 2); if (r.equals(">") || r.equals(">>")) { boolean append = r.length() == 2; String file = t.at(len - 1); try { redirect = new PrintStream(new BufferedOutputStream(new FileOutputStream(file, append))); t.trim(2); } catch (Exception e) { out.println("Error: " + e); if (verboseExceptions) { e.printStackTrace(out); } error = true; } } } if (!error) { PrintStream savedout = out; if (redirect != null) { out = redirect; } try { executeCommand(t); } catch (Exception e) { err.println("Error: " + e); if (verboseExceptions) { e.printStackTrace(err); } } finally { if (redirect != null) { out = savedout; redirect.close(); } } } } } void executeCommand(Tokens args) { String cmd = args.nextToken(); Command doit = findCommand(cmd); /* * Check for an unknown command */ if (doit == null) { out.println("Unrecognized command. Try help..."); } else if (!debugger.isAttached() && !doit.okIfDisconnected) { out.println("Command not valid until the attached to a VM"); } else { try { doit.doit(args); } catch (Exception e) { out.println("Error: " + e); if (verboseExceptions) { e.printStackTrace(out); } } } } }