| /* |
| * Copyright (c) 2005, 2017, 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.classfile.ClassLoaderDataGraph; |
| 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")) { |
| ClassLoaderDataGraph cldg = VM.getVM().getClassLoaderDataGraph(); |
| cldg.classesDo(new ClassLoaderDataGraph.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 { |
| ClassLoaderDataGraph cldg = VM.getVM().getClassLoaderDataGraph(); |
| cldg.classesDo(new ClassLoaderDataGraph.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 attached to a VM"); |
| } else { |
| try { |
| doit.doit(args); |
| } catch (Exception e) { |
| out.println("Error: " + e); |
| if (verboseExceptions) { |
| e.printStackTrace(out); |
| } |
| } |
| } |
| } |
| } |