| /* |
| * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| * |
| */ |
| |
| package sun.jvm.hotspot.ui.classbrowser; |
| |
| import java.io.*; |
| import java.util.*; |
| import sun.jvm.hotspot.asm.*; |
| import sun.jvm.hotspot.asm.sparc.*; |
| import sun.jvm.hotspot.asm.x86.*; |
| import sun.jvm.hotspot.asm.ia64.*; |
| import sun.jvm.hotspot.code.*; |
| import sun.jvm.hotspot.compiler.*; |
| import sun.jvm.hotspot.debugger.*; |
| import sun.jvm.hotspot.interpreter.*; |
| import sun.jvm.hotspot.memory.*; |
| import sun.jvm.hotspot.oops.*; |
| import sun.jvm.hotspot.runtime.*; |
| import sun.jvm.hotspot.tools.jcore.*; |
| import sun.jvm.hotspot.types.*; |
| import sun.jvm.hotspot.utilities.*; |
| |
| public class HTMLGenerator implements /* imports */ ClassConstants { |
| static class Formatter { |
| boolean html; |
| StringBuffer buf = new StringBuffer(); |
| |
| Formatter(boolean h) { |
| html = h; |
| } |
| |
| void append(String s) { |
| buf.append(s); |
| } |
| |
| void append(int s) { |
| buf.append(s); |
| } |
| |
| void append(char s) { |
| buf.append(s); |
| } |
| |
| void append(StringBuffer s) { |
| buf.append(s); |
| } |
| |
| void append(Formatter s) { |
| buf.append(s); |
| } |
| |
| StringBuffer getBuffer() { |
| return buf; |
| } |
| |
| public String toString() { |
| return buf.toString(); |
| } |
| |
| void wrap(String tag, String text) { |
| wrap(tag, tag, text); |
| } |
| void wrap(String before, String after, String text) { |
| beginTag(before); |
| append(text); |
| endTag(after); |
| } |
| |
| // header tags |
| void h1(String s) { nl(); wrap("h1", s); nl(); } |
| void h2(String s) { nl(); wrap("h2", s); nl(); } |
| void h3(String s) { nl(); wrap("h3", s); nl(); } |
| void h4(String s) { nl(); wrap("h4", s); nl(); } |
| |
| // list tags |
| void beginList() { beginTag("ul"); nl(); } |
| void li(String s) { wrap("li", s); nl(); } |
| void endList() { endTag("ul"); nl(); } |
| |
| // table tags |
| void beginTable(int border) { |
| beginTag("table border='" + border + "'"); |
| } |
| void cell(String s) { wrap("td", s); } |
| void headerCell(String s) { wrap("th", s); } |
| void endTable() { endTag("table"); } |
| |
| void link(String href, String text) { |
| wrap("a href='" + href + "'", "a", text); |
| } |
| void beginTag(String s) { |
| if (html) { append("<"); append(s); append(">"); } |
| } |
| void endTag(String s) { |
| if (html) { |
| append("</"); append(s); append(">"); |
| } else { |
| if (s.equals("table") || s.equals("tr")) { |
| nl(); |
| } |
| if (s.equals("td") || s.equals("th")) { |
| append(" "); |
| } |
| } |
| } |
| void bold(String s) { |
| wrap("b", s); |
| } |
| |
| void nl() { |
| if (!html) buf.append("\n"); |
| } |
| |
| void br() { |
| if (html) append("<br>"); |
| else append("\n"); |
| } |
| void genEmptyHTML() { |
| if (html) append("<html></html>"); |
| } |
| |
| void genHTMLPrologue() { |
| if (html) append("<html><body>"); |
| } |
| |
| void genHTMLPrologue(String title) { |
| if (html) { |
| append("<html><head><title>"); |
| append(title); |
| append("</title></head>"); |
| append("<body>"); |
| } |
| h2(title); |
| } |
| void genHTMLEpilogue() { |
| if (html) append("</body></html>"); |
| } |
| |
| } |
| |
| private static final String DUMP_KLASS_OUTPUT_DIR = "."; |
| private static final int NATIVE_CODE_SIZE = 200; |
| private final String spaces; |
| private final String tab; |
| |
| private boolean genHTML = true; |
| |
| public HTMLGenerator() { |
| this(true); |
| } |
| |
| public HTMLGenerator(boolean html) { |
| genHTML = html; |
| if (html) { |
| spaces = " "; |
| tab = " "; |
| } else { |
| spaces = " "; |
| tab = " "; |
| } |
| } |
| |
| private static CPUHelper cpuHelper; |
| static { |
| VM.registerVMInitializedObserver(new Observer() { |
| public void update(Observable o, Object data) { |
| initialize(); |
| } |
| }); |
| } |
| |
| private static synchronized void initialize() { |
| String cpu = VM.getVM().getCPU(); |
| if (cpu.equals("sparc")) { |
| cpuHelper = new SPARCHelper(); |
| } else if (cpu.equals("x86")) { |
| cpuHelper = new X86Helper(); |
| } else if (cpu.equals("ia64")) { |
| cpuHelper = new IA64Helper(); |
| } else { |
| throw new RuntimeException("cpu '" + cpu + "' is not yet supported!"); |
| } |
| } |
| |
| protected static synchronized CPUHelper getCPUHelper() { |
| return cpuHelper; |
| } |
| |
| protected String escapeHTMLSpecialChars(String value) { |
| if (!genHTML) return value; |
| |
| Formatter buf = new Formatter(genHTML); |
| int len = value.length(); |
| for (int i=0; i < len; i++) { |
| char c = value.charAt(i); |
| switch (c) { |
| case '<': |
| buf.append("<"); |
| break; |
| case '>': |
| buf.append(">"); |
| break; |
| case '&': |
| buf.append("&"); |
| break; |
| default: |
| buf.append(c); |
| break; |
| } |
| } |
| return buf.toString(); |
| } |
| |
| public String genHTMLForMessage(String message) { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(message); |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } |
| |
| public String genHTMLErrorMessage(Exception exp) { |
| exp.printStackTrace(); |
| return genHTMLForMessage(exp.getClass().getName() + " : " + exp.getMessage()); |
| } |
| |
| public String genHTMLForWait(String message) { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue("Please wait .."); |
| buf.h2(message); |
| return buf.toString(); |
| } |
| |
| protected String genKlassTitle(InstanceKlass klass) { |
| Formatter buf = new Formatter(genHTML); |
| AccessFlags acc = klass.getAccessFlagsObj(); |
| if (acc.isPublic()) { |
| buf.append("public "); |
| } else if (acc.isProtected()) { |
| buf.append("protected "); |
| } else if (acc.isPrivate()) { |
| buf.append("private "); |
| } |
| |
| if (acc.isStatic()) { |
| buf.append("static "); |
| } |
| |
| if (acc.isAbstract() ) { |
| buf.append("abstract "); |
| } else if (acc.isFinal()) { |
| buf.append("final "); |
| } |
| |
| if (acc.isStrict()) { |
| buf.append("strict "); |
| } |
| |
| // javac generated flags |
| if (acc.isEnum()) { |
| buf.append("[enum] "); |
| } |
| if (acc.isSynthetic()) { |
| buf.append("[synthetic] "); |
| } |
| |
| if (klass.isInterface()) { |
| buf.append("interface"); |
| } else { |
| buf.append("class"); |
| } |
| |
| buf.append(' '); |
| buf.append(klass.getName().asString().replace('/', '.')); |
| // is it generic? |
| Symbol genSig = klass.getGenericSignature(); |
| if (genSig != null) { |
| buf.append(" [signature "); |
| buf.append(escapeHTMLSpecialChars(genSig.asString())); |
| buf.append("] "); |
| } else { |
| buf.append(' '); |
| } |
| buf.append('@'); |
| buf.append(klass.getHandle().toString()); |
| return buf.toString(); |
| } |
| |
| protected String genBaseHref() { |
| return ""; |
| } |
| |
| protected String genKlassHref(InstanceKlass klass) { |
| return genBaseHref() + "klass=" + klass.getHandle(); |
| } |
| |
| protected String genKlassLink(InstanceKlass klass) { |
| Formatter buf = new Formatter(genHTML); |
| buf.link(genKlassHref(klass), genKlassTitle(klass)); |
| return buf.toString(); |
| } |
| |
| protected String genMethodModifierString(AccessFlags acc) { |
| Formatter buf = new Formatter(genHTML); |
| if (acc.isPrivate()) { |
| buf.append("private "); |
| } else if (acc.isProtected()) { |
| buf.append("protected "); |
| } else if (acc.isPublic()) { |
| buf.append("public "); |
| } |
| |
| if (acc.isStatic()) { |
| buf.append("static "); |
| } else if (acc.isAbstract() ) { |
| buf.append("abstract "); |
| } else if (acc.isFinal()) { |
| buf.append("final "); |
| } |
| |
| if (acc.isNative()) { |
| buf.append("native "); |
| } |
| |
| if (acc.isStrict()) { |
| buf.append("strict "); |
| } |
| |
| if (acc.isSynchronized()) { |
| buf.append("synchronized "); |
| } |
| |
| // javac generated flags |
| if (acc.isBridge()) { |
| buf.append("[bridge] "); |
| } |
| |
| if (acc.isSynthetic()) { |
| buf.append("[synthetic] "); |
| } |
| |
| if (acc.isVarArgs()) { |
| buf.append("[varargs] "); |
| } |
| |
| return buf.toString(); |
| } |
| |
| protected String genMethodNameAndSignature(Method method) { |
| Formatter buf = new Formatter(genHTML); |
| buf.append(genMethodModifierString(method.getAccessFlagsObj())); |
| Symbol sig = method.getSignature(); |
| new SignatureConverter(sig, buf.getBuffer()).iterateReturntype(); |
| buf.append(" "); |
| String methodName = method.getName().asString(); |
| buf.append(escapeHTMLSpecialChars(methodName)); |
| buf.append('('); |
| new SignatureConverter(sig, buf.getBuffer()).iterateParameters(); |
| buf.append(')'); |
| // is it generic? |
| Symbol genSig = method.getGenericSignature(); |
| if (genSig != null) { |
| buf.append(" [signature "); |
| buf.append(escapeHTMLSpecialChars(genSig.asString())); |
| buf.append("] "); |
| } |
| return buf.toString().replace('/', '.'); |
| } |
| |
| protected String genMethodTitle(Method method) { |
| Formatter buf = new Formatter(genHTML); |
| buf.append(genMethodNameAndSignature(method)); |
| buf.append(' '); |
| buf.append('@'); |
| buf.append(method.getHandle().toString()); |
| return buf.toString(); |
| } |
| |
| protected String genMethodHref(Method m) { |
| return genBaseHref() + "method=" + m.getHandle(); |
| } |
| |
| protected String genMethodLink(Method m) { |
| Formatter buf = new Formatter(genHTML); |
| buf.link(genMethodHref(m), genMethodTitle(m)); |
| return buf.toString(); |
| } |
| |
| protected String genMethodAndKlassLink(Method m) { |
| Formatter buf = new Formatter(genHTML); |
| buf.append(genMethodLink(m)); |
| buf.append(" of "); |
| buf.append(genKlassLink((InstanceKlass) m.getMethodHolder())); |
| return buf.toString(); |
| } |
| |
| protected String genNMethodHref(NMethod nm) { |
| return genBaseHref() + "nmethod=" + nm.getAddress(); |
| } |
| |
| public String genNMethodTitle(NMethod nmethod) { |
| Formatter buf = new Formatter(genHTML); |
| Method m = nmethod.getMethod(); |
| |
| buf.append("Disassembly for compiled method ["); |
| buf.append(genMethodTitle(m)); |
| buf.append(" ] "); |
| buf.append('@'); |
| buf.append(nmethod.getAddress().toString()); |
| return buf.toString(); |
| } |
| |
| protected String genNMethodLink(NMethod nm) { |
| Formatter buf = new Formatter(genHTML); |
| buf.link(genNMethodHref(nm), genNMethodTitle(nm)); |
| return buf.toString(); |
| } |
| |
| public String genCodeBlobTitle(CodeBlob blob) { |
| Formatter buf = new Formatter(genHTML); |
| buf.append("Disassembly for code blob " + blob.getName() + " ["); |
| buf.append(blob.getClass().getName()); |
| buf.append(" ] @"); |
| buf.append(blob.getAddress().toString()); |
| return buf.toString(); |
| } |
| |
| protected BytecodeDisassembler createBytecodeDisassembler(Method m) { |
| return new BytecodeDisassembler(m); |
| } |
| |
| private String genLowHighShort(int val) { |
| Formatter buf = new Formatter(genHTML); |
| buf.append('#'); |
| buf.append(Integer.toString(val & 0xFFFF)); |
| buf.append(" #"); |
| buf.append(Integer.toString((val >> 16) & 0xFFFF)); |
| return buf.toString(); |
| } |
| |
| protected String genHTMLTableForConstantPool(ConstantPool cpool) { |
| Formatter buf = new Formatter(genHTML); |
| buf.beginTable(1); |
| |
| buf.beginTag("tr"); |
| buf.headerCell("Index"); |
| buf.headerCell("Constant Type"); |
| buf.headerCell("Constant Value"); |
| buf.endTag("tr"); |
| |
| final int length = (int) cpool.getLength(); |
| // zero'th pool entry is always invalid. ignore it. |
| for (int index = 1; index < length; index++) { |
| buf.beginTag("tr"); |
| buf.cell(Integer.toString(index)); |
| |
| int ctag = (int) cpool.getTags().getByteAt((int) index); |
| switch (ctag) { |
| case JVM_CONSTANT_Integer: |
| buf.cell("JVM_CONSTANT_Integer"); |
| buf.cell(Integer.toString(cpool.getIntAt(index))); |
| break; |
| |
| case JVM_CONSTANT_Float: |
| buf.cell("JVM_CONSTANT_Float"); |
| buf.cell(Float.toString(cpool.getFloatAt(index))); |
| break; |
| |
| case JVM_CONSTANT_Long: |
| buf.cell("JVM_CONSTANT_Long"); |
| buf.cell(Long.toString(cpool.getLongAt(index))); |
| // long entries occupy two slots |
| index++; |
| break; |
| |
| case JVM_CONSTANT_Double: |
| buf.cell("JVM_CONSTANT_Double"); |
| buf.cell(Double.toString(cpool.getDoubleAt(index))); |
| // double entries occupy two slots |
| index++; |
| break; |
| |
| case JVM_CONSTANT_UnresolvedClass: |
| buf.cell("JVM_CONSTANT_UnresolvedClass"); |
| buf.cell(cpool.getSymbolAt(index).asString()); |
| break; |
| |
| case JVM_CONSTANT_Class: |
| buf.cell("JVM_CONSTANT_Class"); |
| Klass klass = (Klass) cpool.getObjAt(index); |
| if (klass instanceof InstanceKlass) { |
| buf.cell(genKlassLink((InstanceKlass) klass)); |
| } else { |
| buf.cell(klass.getName().asString().replace('/', '.')); |
| } |
| break; |
| |
| case JVM_CONSTANT_UnresolvedString: |
| buf.cell("JVM_CONSTANT_UnresolvedString"); |
| buf.cell("\"" + |
| escapeHTMLSpecialChars(cpool.getSymbolAt(index).asString()) + |
| "\""); |
| break; |
| |
| case JVM_CONSTANT_Utf8: |
| buf.cell("JVM_CONSTANT_Utf8"); |
| buf.cell("\"" + |
| escapeHTMLSpecialChars(cpool.getSymbolAt(index).asString()) + |
| "\""); |
| break; |
| |
| case JVM_CONSTANT_String: |
| buf.cell("JVM_CONSTANT_String"); |
| buf.cell("\"" + |
| escapeHTMLSpecialChars(OopUtilities.stringOopToString(cpool.getObjAt(index))) + "\""); |
| break; |
| |
| case JVM_CONSTANT_Fieldref: |
| buf.cell("JVM_CONSTANT_Fieldref"); |
| buf.cell(genLowHighShort(cpool.getIntAt(index))); |
| break; |
| |
| case JVM_CONSTANT_Methodref: |
| buf.cell("JVM_CONSTANT_Methodref"); |
| buf.cell(genLowHighShort(cpool.getIntAt(index))); |
| break; |
| |
| case JVM_CONSTANT_InterfaceMethodref: |
| buf.cell("JVM_CONSTANT_InterfaceMethodref"); |
| buf.cell(genLowHighShort(cpool.getIntAt(index))); |
| break; |
| |
| case JVM_CONSTANT_NameAndType: |
| buf.cell("JVM_CONSTANT_NameAndType"); |
| buf.cell(genLowHighShort(cpool.getIntAt(index))); |
| break; |
| |
| case JVM_CONSTANT_ClassIndex: |
| buf.cell("JVM_CONSTANT_ClassIndex"); |
| buf.cell(Integer.toString(cpool.getIntAt(index))); |
| break; |
| |
| case JVM_CONSTANT_StringIndex: |
| buf.cell("JVM_CONSTANT_StringIndex"); |
| buf.cell(Integer.toString(cpool.getIntAt(index))); |
| break; |
| } |
| |
| buf.endTag("tr"); |
| } |
| |
| buf.endTable(); |
| return buf.toString(); |
| } |
| |
| public String genHTML(ConstantPool cpool) { |
| try { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(genConstantPoolTitle(cpool)); |
| buf.h3("Holder Class"); |
| buf.append(genKlassLink((InstanceKlass) cpool.getPoolHolder())); |
| buf.h3("Constants"); |
| buf.append(genHTMLTableForConstantPool(cpool)); |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected String genConstantPoolHref(ConstantPool cpool) { |
| return genBaseHref() + "cpool=" + cpool.getHandle(); |
| } |
| |
| protected String genConstantPoolTitle(ConstantPool cpool) { |
| Formatter buf = new Formatter(genHTML); |
| buf.append("Constant Pool of ["); |
| buf.append(genKlassTitle((InstanceKlass) cpool.getPoolHolder())); |
| buf.append("] @"); |
| buf.append(cpool.getHandle().toString()); |
| return buf.toString(); |
| } |
| |
| protected String genConstantPoolLink(ConstantPool cpool) { |
| Formatter buf = new Formatter(genHTML); |
| buf.link(genConstantPoolHref(cpool), genConstantPoolTitle(cpool)); |
| return buf.toString(); |
| } |
| |
| public String genHTML(Method method) { |
| try { |
| final Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(genMethodTitle(method)); |
| |
| buf.h3("Holder Class"); |
| buf.append(genKlassLink((InstanceKlass) method.getMethodHolder())); |
| |
| NMethod nmethod = method.getNativeMethod(); |
| if (nmethod != null) { |
| buf.h3("Compiled Code"); |
| buf.append(genNMethodLink(nmethod)); |
| } |
| |
| boolean hasThrows = method.hasCheckedExceptions(); |
| ConstantPool cpool = ((InstanceKlass) method.getMethodHolder()).getConstants(); |
| if (hasThrows) { |
| buf.h3("Checked Exception(s)"); |
| CheckedExceptionElement[] exceptions = method.getCheckedExceptions(); |
| buf.beginTag("ul"); |
| for (int exp = 0; exp < exceptions.length; exp++) { |
| short cpIndex = (short) exceptions[exp].getClassCPIndex(); |
| Oop obj = cpool.getObjAt(cpIndex); |
| if (obj instanceof Symbol) { |
| buf.li(((Symbol)obj).asString().replace('/', '.')); |
| } else { |
| buf.li(genKlassLink((InstanceKlass)obj)); |
| } |
| } |
| buf.endTag("ul"); |
| } |
| |
| if (method.isNative() || method.isAbstract()) { |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } |
| |
| buf.h3("Bytecode"); |
| BytecodeDisassembler disasm = createBytecodeDisassembler(method); |
| final boolean hasLineNumbers = method.hasLineNumberTable(); |
| disasm.decode(new BytecodeVisitor() { |
| private Method method; |
| public void prologue(Method m) { |
| method = m; |
| buf.beginTable(0); |
| buf.beginTag("tr"); |
| if (hasLineNumbers) { |
| buf.headerCell("line"); |
| } |
| buf.headerCell("bci" + spaces); |
| buf.headerCell("bytecode"); |
| buf.endTag("tr"); |
| } |
| |
| public void visit(Bytecode instr) { |
| int curBci = instr.bci(); |
| buf.beginTag("tr"); |
| if (hasLineNumbers) { |
| int lineNumber = method.getLineNumberFromBCI(curBci); |
| buf.cell(Integer.toString(lineNumber) + spaces); |
| } |
| buf.cell(Integer.toString(curBci) + spaces); |
| |
| buf.beginTag("td"); |
| String instrStr = escapeHTMLSpecialChars(instr.toString()); |
| |
| if (instr instanceof BytecodeNew) { |
| BytecodeNew newBytecode = (BytecodeNew) instr; |
| InstanceKlass klass = newBytecode.getNewKlass(); |
| if (klass != null) { |
| buf.link(genKlassHref(klass), instrStr); |
| } else { |
| buf.append(instrStr); |
| } |
| } else if(instr instanceof BytecodeInvoke) { |
| BytecodeInvoke invokeBytecode = (BytecodeInvoke) instr; |
| Method m = invokeBytecode.getInvokedMethod(); |
| if (m != null) { |
| buf.link(genMethodHref(m), instrStr); |
| buf.append(" of "); |
| InstanceKlass klass = (InstanceKlass) m.getMethodHolder(); |
| buf.link(genKlassHref(klass), genKlassTitle(klass)); |
| } else { |
| buf.append(instrStr); |
| } |
| } else if (instr instanceof BytecodeGetPut) { |
| BytecodeGetPut getPut = (BytecodeGetPut) instr; |
| sun.jvm.hotspot.oops.Field f = getPut.getField(); |
| buf.append(instrStr); |
| if (f != null) { |
| InstanceKlass klass = f.getFieldHolder(); |
| buf.append(" of "); |
| buf.link(genKlassHref(klass), genKlassTitle(klass)); |
| } |
| } else if (instr instanceof BytecodeLoadConstant) { |
| BytecodeLoadConstant ldc = (BytecodeLoadConstant) instr; |
| if (ldc.isKlassConstant()) { |
| Oop oop = ldc.getKlass(); |
| if (oop instanceof Klass) { |
| buf.append("<a href='"); |
| buf.append(genKlassHref((InstanceKlass) oop)); |
| buf.append("'>"); |
| buf.append(instrStr); |
| buf.append("</a>"); |
| } else { |
| // unresolved klass literal |
| buf.append(instrStr); |
| } |
| } else { |
| // not a klass literal |
| buf.append(instrStr); |
| } |
| } else { |
| buf.append(instrStr); |
| } |
| buf.endTag("td"); |
| buf.endTag("tr"); |
| } |
| |
| public void epilogue() { |
| buf.endTable(); |
| } |
| }); |
| |
| // display exception table for this method |
| TypeArray exceptionTable = method.getExceptionTable(); |
| // exception table is 4 tuple array of shorts |
| int numEntries = (int)exceptionTable.getLength() / 4; |
| if (numEntries != 0) { |
| buf.h4("Exception Table"); |
| buf.beginTable(1); |
| buf.beginTag("tr"); |
| buf.headerCell("start bci"); |
| buf.headerCell("end bci"); |
| buf.headerCell("handler bci"); |
| buf.headerCell("catch type"); |
| buf.endTag("tr"); |
| |
| for (int e = 0; e < numEntries; e += 4) { |
| buf.beginTag("tr"); |
| buf.cell(Integer.toString(exceptionTable.getIntAt(e))); |
| buf.cell(Integer.toString(exceptionTable.getIntAt(e + 1))); |
| buf.cell(Integer.toString(exceptionTable.getIntAt(e + 2))); |
| short cpIndex = (short) exceptionTable.getIntAt(e + 3); |
| Oop obj = cpIndex == 0? null : cpool.getObjAt(cpIndex); |
| if (obj == null) { |
| buf.cell("Any"); |
| } else if (obj instanceof Symbol) { |
| buf.cell(((Symbol)obj).asString().replace('/', '.')); |
| } else { |
| buf.cell(genKlassLink((InstanceKlass)obj)); |
| } |
| buf.endTag("tr"); |
| } |
| |
| buf.endTable(); |
| } |
| |
| // display constant pool hyperlink |
| buf.h3("Constant Pool"); |
| buf.append(genConstantPoolLink(cpool)); |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected Disassembler createDisassembler(long startPc, byte[] code) { |
| return getCPUHelper().createDisassembler(startPc, code); |
| } |
| |
| protected SymbolFinder createSymbolFinder() { |
| return new DummySymbolFinder(); |
| } |
| |
| // genHTML for a given address. Address may be a PC or |
| // methodOop or klassOop. |
| |
| public String genHTMLForAddress(String addrStr) { |
| return genHTML(parseAddress(addrStr)); |
| } |
| |
| public String genHTML(sun.jvm.hotspot.debugger.Address pc) { |
| CodeBlob blob = null; |
| |
| try { |
| blob = (CodeBlob)VM.getVM().getCodeCache().findBlobUnsafe(pc); |
| } catch (Exception exp) { |
| // ignore |
| } |
| |
| if (blob != null) { |
| if (blob instanceof NMethod) { |
| return genHTML((NMethod)blob); |
| } else { |
| // may be interpreter code. |
| Interpreter interp = VM.getVM().getInterpreter(); |
| if (interp.contains(pc)) { |
| InterpreterCodelet codelet = interp.getCodeletContaining(pc); |
| return genHTML(codelet); |
| } |
| return genHTML(blob); |
| } |
| } else if (VM.getVM().getCodeCache().contains(pc)) { |
| return "Unknown location in the CodeCache: " + pc; |
| } |
| |
| // did not find nmethod. |
| // try methodOop, klassOop and constantPoolOop. |
| try { |
| Oop obj = getOopAtAddress(pc); |
| if (obj != null) { |
| if (obj instanceof Method) { |
| return genHTML((Method) obj); |
| } else if (obj instanceof InstanceKlass) { |
| return genHTML((InstanceKlass) obj); |
| } else if (obj instanceof ConstantPool) { |
| return genHTML((ConstantPool) obj); |
| } |
| } |
| } catch (Exception exp) { |
| // ignore |
| } |
| |
| // didn't find any. do raw disassembly. |
| return genHTMLForRawDisassembly(pc, null); |
| } |
| |
| protected byte[] readBuffer(sun.jvm.hotspot.debugger.Address addr, int size) { |
| byte[] buf = new byte[size]; |
| for (int b = 0; b < size; b++) { |
| buf[b] = (byte) addr.getJByteAt(b); |
| } |
| return buf; |
| } |
| |
| public String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address startPc, int size) { |
| try { |
| return genHTMLForRawDisassembly(startPc, null, readBuffer(startPc, size)); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address startPc, |
| String prevPCs) { |
| try { |
| return genHTMLForRawDisassembly(startPc, prevPCs, readBuffer(startPc, NATIVE_CODE_SIZE)); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected String genPCHref(long targetPc) { |
| return genBaseHref() + "pc=0x" + Long.toHexString(targetPc); |
| } |
| |
| protected String genMultPCHref(String pcs) { |
| StringBuffer buf = new StringBuffer(genBaseHref()); |
| buf.append("pc_multiple="); |
| buf.append(pcs); |
| return buf.toString(); |
| } |
| |
| protected String genPCHref(long currentPc, sun.jvm.hotspot.asm.Address addr) { |
| String href = null; |
| if (addr instanceof PCRelativeAddress) { |
| PCRelativeAddress pcRelAddr = (PCRelativeAddress) addr; |
| href = genPCHref(currentPc + pcRelAddr.getDisplacement()); |
| } else if(addr instanceof DirectAddress) { |
| href = genPCHref(((DirectAddress) addr).getValue()); |
| } |
| |
| return href; |
| } |
| |
| class RawCodeVisitor implements InstructionVisitor { |
| private int instrSize = 0; |
| private Formatter buf; |
| private SymbolFinder symFinder = createSymbolFinder(); |
| |
| RawCodeVisitor(Formatter buf) { |
| this.buf = buf; |
| } |
| |
| public int getInstructionSize() { |
| return instrSize; |
| } |
| |
| public void prologue() { |
| } |
| |
| public void visit(long currentPc, Instruction instr) { |
| String href = null; |
| if (instr.isCall()) { |
| CallInstruction call = (CallInstruction) instr; |
| sun.jvm.hotspot.asm.Address addr = call.getBranchDestination(); |
| href = genPCHref(currentPc, addr); |
| } |
| |
| instrSize += instr.getSize(); |
| buf.append("0x"); |
| buf.append(Long.toHexString(currentPc)); |
| buf.append(':'); |
| buf.append(tab); |
| |
| if (href != null) { |
| buf.link(href, instr.asString(currentPc, symFinder)); |
| } else { |
| buf.append(instr.asString(currentPc, symFinder)); |
| } |
| buf.br(); |
| } |
| |
| public void epilogue() { |
| } |
| }; |
| |
| protected String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address addr, |
| String prevPCs, |
| byte[] code) { |
| try { |
| long startPc = addressToLong(addr); |
| Disassembler disasm = createDisassembler(startPc, code); |
| final Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue("Disassembly @0x" + Long.toHexString(startPc)); |
| |
| if (prevPCs != null && genHTML) { |
| buf.beginTag("p"); |
| buf.link(genMultPCHref(prevPCs), "show previous code .."); |
| buf.endTag("p"); |
| } |
| |
| |
| buf.h3("Code"); |
| RawCodeVisitor visitor = new RawCodeVisitor(buf); |
| disasm.decode(visitor); |
| |
| if (genHTML) buf.beginTag("p"); |
| Formatter tmpBuf = new Formatter(genHTML); |
| tmpBuf.append("0x"); |
| tmpBuf.append(Long.toHexString(startPc + visitor.getInstructionSize()).toString()); |
| tmpBuf.append(",0x"); |
| tmpBuf.append(Long.toHexString(startPc)); |
| if (prevPCs != null) { |
| tmpBuf.append(','); |
| tmpBuf.append(prevPCs); |
| } |
| if (genHTML) { |
| buf.link(genMultPCHref(tmpBuf.toString()), "show more code .."); |
| buf.endTag("p"); |
| } |
| |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected String genSafepointInfo(NMethod nm, PCDesc pcDesc) { |
| ScopeDesc sd = nm.getScopeDescAt(pcDesc.getRealPC(nm)); |
| Formatter buf = new Formatter(genHTML); |
| Formatter tabs = new Formatter(genHTML); |
| |
| buf.beginTag("pre"); |
| genScope(buf, tabs, sd); |
| buf.endTag("pre"); |
| buf.append(genOopMapInfo(nm, pcDesc)); |
| |
| return buf.toString(); |
| } |
| |
| protected void genScope(Formatter buf, Formatter tabs, ScopeDesc sd) { |
| if (sd == null) { |
| return; |
| } |
| |
| genScope(buf, tabs, sd.sender()); |
| |
| buf.append(tabs); |
| Method m = sd.getMethod(); |
| buf.append(genMethodAndKlassLink(m)); |
| int bci = sd.getBCI(); |
| buf.append(" @ bci = "); |
| buf.append(Integer.toString(bci)); |
| |
| int line = m.getLineNumberFromBCI(bci); |
| if (line != -1) { |
| buf.append(", line = "); |
| buf.append(Integer.toString(line)); |
| } |
| |
| List locals = sd.getLocals(); |
| if (locals != null) { |
| buf.br(); |
| buf.append(tabs); |
| buf.append(genHTMLForLocals(sd, locals)); |
| } |
| |
| List expressions = sd.getExpressions(); |
| if (expressions != null) { |
| buf.br(); |
| buf.append(tabs); |
| buf.append(genHTMLForExpressions(sd, expressions)); |
| } |
| |
| List monitors = sd.getMonitors(); |
| if (monitors != null) { |
| buf.br(); |
| buf.append(tabs); |
| buf.append(genHTMLForMonitors(sd, monitors)); |
| } |
| |
| tabs.append(tab); |
| buf.br(); |
| } |
| |
| protected String genHTMLForOopMap(OopMap map) { |
| final int stack0 = VMRegImpl.getStack0().getValue(); |
| Formatter buf = new Formatter(genHTML); |
| |
| final class OopMapValueIterator { |
| final Formatter iterate(OopMapStream oms, String type, boolean printContentReg) { |
| Formatter tmpBuf = new Formatter(genHTML); |
| boolean found = false; |
| tmpBuf.beginTag("tr"); |
| tmpBuf.beginTag("td"); |
| tmpBuf.append(type); |
| tmpBuf.endTag("td"); |
| tmpBuf.endTag("tr"); |
| for (; ! oms.isDone(); oms.next()) { |
| OopMapValue omv = oms.getCurrent(); |
| if (omv == null) { |
| continue; |
| } |
| found = true; |
| VMReg vmReg = omv.getReg(); |
| int reg = vmReg.getValue(); |
| if (reg < stack0) { |
| tmpBuf.append(VMRegImpl.getRegisterName(vmReg.getValue())); |
| } else { |
| tmpBuf.append('['); |
| tmpBuf.append(Integer.toString((reg - stack0) * 4)); |
| tmpBuf.append(']'); |
| } |
| if (printContentReg) { |
| tmpBuf.append(" = "); |
| VMReg vmContentReg = omv.getContentReg(); |
| int contentReg = vmContentReg.getValue(); |
| tmpBuf.append(VMRegImpl.getRegisterName(vmContentReg.getValue())); |
| } |
| tmpBuf.append(spaces); |
| } |
| tmpBuf.endTag("td"); |
| tmpBuf.endTag("tr"); |
| return found ? tmpBuf : new Formatter(genHTML); |
| } |
| } |
| |
| buf.beginTable(0); |
| |
| OopMapValueIterator omvIterator = new OopMapValueIterator(); |
| OopMapStream oms = new OopMapStream(map, OopMapValue.OopTypes.OOP_VALUE); |
| buf.append(omvIterator.iterate(oms, "Oop:", false)); |
| |
| oms = new OopMapStream(map, OopMapValue.OopTypes.VALUE_VALUE); |
| buf.append(omvIterator.iterate(oms, "Value:", false)); |
| |
| oms = new OopMapStream(map, OopMapValue.OopTypes.DEAD_VALUE); |
| buf.append(omvIterator.iterate(oms, "Dead:", false)); |
| |
| oms = new OopMapStream(map, OopMapValue.OopTypes.CALLEE_SAVED_VALUE); |
| buf.append(omvIterator.iterate(oms, "Callee saved:", true)); |
| |
| oms = new OopMapStream(map, OopMapValue.OopTypes.DERIVED_OOP_VALUE); |
| buf.append(omvIterator.iterate(oms, "Derived oop:", true)); |
| |
| buf.endTag("table"); |
| return buf.toString(); |
| } |
| |
| |
| protected String genOopMapInfo(NMethod nmethod, PCDesc pcDesc) { |
| OopMapSet mapSet = nmethod.getOopMaps(); |
| int pcOffset = pcDesc.getPCOffset(); |
| OopMap map = mapSet.findMapAtOffset(pcOffset, VM.getVM().isDebugging()); |
| if (map == null) { |
| throw new IllegalArgumentException("no oopmap at safepoint!"); |
| } |
| |
| return genOopMapInfo(map); |
| } |
| |
| protected String genOopMapInfo(OopMap map) { |
| Formatter buf = new Formatter(genHTML); |
| buf.beginTag("pre"); |
| buf.append("OopMap: "); |
| buf.append(genHTMLForOopMap(map)); |
| buf.endTag("pre"); |
| |
| return buf.toString(); |
| } |
| |
| protected String locationAsString(Location loc) { |
| Formatter buf = new Formatter(genHTML); |
| if (loc.isIllegal()) { |
| buf.append("illegal"); |
| } else { |
| Location.Where w = loc.getWhere(); |
| Location.Type type = loc.getType(); |
| |
| if (w == Location.Where.ON_STACK) { |
| buf.append("stack[" + loc.getStackOffset() + "]"); |
| } else if (w == Location.Where.IN_REGISTER) { |
| boolean isFloat = (type == Location.Type.FLOAT_IN_DBL || |
| type == Location.Type.DBL); |
| int regNum = loc.getRegisterNumber(); |
| VMReg vmReg = new VMReg(regNum); |
| buf.append(VMRegImpl.getRegisterName(vmReg.getValue())); |
| } |
| |
| buf.append(", "); |
| if (type == Location.Type.NORMAL) { |
| buf.append("normal"); |
| } else if (type == Location.Type.OOP) { |
| buf.append("oop"); |
| } else if (type == Location.Type.INT_IN_LONG) { |
| buf.append("int"); |
| } else if (type == Location.Type.LNG) { |
| buf.append("long"); |
| } else if (type == Location.Type.FLOAT_IN_DBL) { |
| buf.append("float"); |
| } else if (type == Location.Type.DBL) { |
| buf.append("double"); |
| } else if (type == Location.Type.ADDR) { |
| buf.append("address"); |
| } else if (type == Location.Type.INVALID) { |
| buf.append("invalid"); |
| } |
| } |
| return buf.toString(); |
| } |
| |
| private String scopeValueAsString(ScopeValue sv) { |
| Formatter buf = new Formatter(genHTML); |
| if (sv.isConstantInt()) { |
| buf.append("int "); |
| ConstantIntValue intValue = (ConstantIntValue) sv; |
| buf.append(Integer.toString(intValue.getValue())); |
| } else if (sv.isConstantLong()) { |
| buf.append("long "); |
| ConstantLongValue longValue = (ConstantLongValue) sv; |
| buf.append(Long.toString(longValue.getValue())); |
| buf.append("L"); |
| } else if (sv.isConstantDouble()) { |
| buf.append("double "); |
| ConstantDoubleValue dblValue = (ConstantDoubleValue) sv; |
| buf.append(Double.toString(dblValue.getValue())); |
| buf.append("D"); |
| } else if (sv.isConstantOop()) { |
| buf.append("oop "); |
| ConstantOopReadValue oopValue = (ConstantOopReadValue) sv; |
| OopHandle oopHandle = oopValue.getValue(); |
| if (oopHandle != null) { |
| buf.append(oopHandle.toString()); |
| } else { |
| buf.append("null"); |
| } |
| } else if (sv.isLocation()) { |
| LocationValue lvalue = (LocationValue) sv; |
| Location loc = lvalue.getLocation(); |
| if (loc != null) { |
| buf.append(locationAsString(loc)); |
| } else { |
| buf.append("null"); |
| } |
| } |
| return buf.toString(); |
| } |
| |
| protected String genHTMLForScopeValues(ScopeDesc sd, boolean locals, List values) { |
| int length = values.size(); |
| Formatter buf = new Formatter(genHTML); |
| buf.append(locals? "locals " : "expressions "); |
| for (int i = 0; i < length; i++) { |
| ScopeValue sv = (ScopeValue) values.get(i); |
| if (sv == null) { |
| continue; |
| } |
| buf.append('('); |
| if (locals) { |
| Symbol name = sd.getMethod().getLocalVariableName(sd.getBCI(), i); |
| if (name != null) { |
| buf.append("'"); |
| buf.append(name.asString()); |
| buf.append('\''); |
| } else { |
| buf.append("["); |
| buf.append(Integer.toString(i)); |
| buf.append(']'); |
| } |
| } else { |
| buf.append("["); |
| buf.append(Integer.toString(i)); |
| buf.append(']'); |
| } |
| |
| buf.append(", "); |
| buf.append(scopeValueAsString(sv)); |
| buf.append(") "); |
| } |
| |
| return buf.toString(); |
| } |
| |
| protected String genHTMLForLocals(ScopeDesc sd, List locals) { |
| return genHTMLForScopeValues(sd, true, locals); |
| } |
| |
| protected String genHTMLForExpressions(ScopeDesc sd, List expressions) { |
| return genHTMLForScopeValues(sd, false, expressions); |
| } |
| |
| protected String genHTMLForMonitors(ScopeDesc sd, List monitors) { |
| int length = monitors.size(); |
| Formatter buf = new Formatter(genHTML); |
| buf.append("monitors "); |
| for (int i = 0; i < length; i++) { |
| MonitorValue mv = (MonitorValue) monitors.get(i); |
| if (mv == null) { |
| continue; |
| } |
| buf.append("(owner = "); |
| ScopeValue owner = mv.owner(); |
| if (owner != null) { |
| buf.append(scopeValueAsString(owner)); |
| } else { |
| buf.append("null"); |
| } |
| buf.append(", lock = "); |
| |
| Location loc = mv.basicLock(); |
| if (loc != null) { |
| buf.append(locationAsString(loc)); |
| } else { |
| buf.append("null"); |
| } |
| buf.append(") "); |
| } |
| return buf.toString(); |
| } |
| |
| public String genHTML(final NMethod nmethod) { |
| try { |
| final Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(genNMethodTitle(nmethod)); |
| buf.h3("Method"); |
| buf.append(genMethodAndKlassLink(nmethod.getMethod())); |
| |
| buf.h3("Compiled Code"); |
| sun.jvm.hotspot.debugger.Address codeBegin = nmethod.codeBegin(); |
| sun.jvm.hotspot.debugger.Address codeEnd = nmethod.codeEnd(); |
| final int codeSize = (int)codeEnd.minus(codeBegin); |
| final long startPc = addressToLong(codeBegin); |
| final byte[] code = new byte[codeSize]; |
| for (int i=0; i < code.length; i++) |
| code[i] = codeBegin.getJByteAt(i); |
| |
| final long verifiedEntryPoint = addressToLong(nmethod.getVerifiedEntryPoint()); |
| final long entryPoint = addressToLong(nmethod.getEntryPoint()); |
| final Map safepoints = nmethod.getSafepoints(); |
| |
| final SymbolFinder symFinder = createSymbolFinder(); |
| final Disassembler disasm = createDisassembler(startPc, code); |
| class NMethodVisitor implements InstructionVisitor { |
| boolean prevWasCall; |
| public void prologue() { |
| prevWasCall = false; |
| } |
| |
| public void visit(long currentPc, Instruction instr) { |
| String href = null; |
| if (instr.isCall()) { |
| CallInstruction call = (CallInstruction) instr; |
| sun.jvm.hotspot.asm.Address addr = call.getBranchDestination(); |
| href = genPCHref(currentPc, addr); |
| } |
| |
| if (currentPc == verifiedEntryPoint) { |
| buf.bold("Verified Entry Point"); buf.br(); |
| } |
| if (currentPc == entryPoint) { |
| buf.bold(">Entry Point"); buf.br(); |
| } |
| |
| PCDesc pcDesc = (PCDesc) safepoints.get(longToAddress(currentPc)); |
| |
| boolean isSafepoint = (pcDesc != null); |
| if (isSafepoint && prevWasCall) { |
| buf.append(genSafepointInfo(nmethod, pcDesc)); |
| } |
| |
| buf.append("0x"); |
| buf.append(Long.toHexString(currentPc)); |
| buf.append(':'); |
| buf.append(tab); |
| |
| if (href != null) { |
| buf.link(href, instr.asString(currentPc, symFinder)); |
| } else { |
| buf.append(instr.asString(currentPc, symFinder)); |
| } |
| |
| if (isSafepoint && !prevWasCall) { |
| buf.append(genSafepointInfo(nmethod, pcDesc)); |
| } |
| |
| buf.br(); |
| prevWasCall = instr.isCall(); |
| } |
| |
| public void epilogue() { |
| } |
| }; |
| |
| disasm.decode(new NMethodVisitor()); |
| |
| sun.jvm.hotspot.debugger.Address stubBegin = nmethod.stubBegin(); |
| if (stubBegin != null) { |
| sun.jvm.hotspot.debugger.Address stubEnd = nmethod.stubEnd(); |
| buf.h3("Stub"); |
| long stubStartPc = addressToLong(stubBegin); |
| long stubEndPc = addressToLong(stubEnd); |
| int range = (int) (stubEndPc - stubStartPc); |
| byte[] stubCode = readBuffer(stubBegin, range); |
| Disassembler disasm2 = createDisassembler(stubStartPc, stubCode); |
| disasm2.decode(new NMethodVisitor()); |
| } |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| public String genHTML(final CodeBlob blob) { |
| try { |
| final Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(genCodeBlobTitle(blob)); |
| buf.h3("CodeBlob"); |
| |
| buf.h3("Compiled Code"); |
| final sun.jvm.hotspot.debugger.Address codeBegin = blob.instructionsBegin(); |
| final int codeSize = blob.getInstructionsSize(); |
| final long startPc = addressToLong(codeBegin); |
| final byte[] code = new byte[codeSize]; |
| for (int i=0; i < code.length; i++) |
| code[i] = codeBegin.getJByteAt(i); |
| |
| final SymbolFinder symFinder = createSymbolFinder(); |
| final Disassembler disasm = createDisassembler(startPc, code); |
| class CodeBlobVisitor implements InstructionVisitor { |
| OopMapSet maps; |
| OopMap curMap; |
| int curMapIndex; |
| long curMapOffset; |
| public void prologue() { |
| maps = blob.getOopMaps(); |
| if (maps != null && (maps.getSize() > 0)) { |
| curMap = maps.getMapAt(0); |
| if (curMap != null) { |
| curMapOffset = curMap.getOffset(); |
| } |
| } |
| } |
| |
| public void visit(long currentPc, Instruction instr) { |
| String href = null; |
| if (instr.isCall()) { |
| CallInstruction call = (CallInstruction) instr; |
| sun.jvm.hotspot.asm.Address addr = call.getBranchDestination(); |
| href = genPCHref(currentPc, addr); |
| } |
| |
| buf.append("0x"); |
| buf.append(Long.toHexString(currentPc)); |
| buf.append(':'); |
| buf.append(tab); |
| |
| if (href != null) { |
| buf.link(href, instr.asString(currentPc, symFinder)); |
| } else { |
| buf.append(instr.asString(currentPc, symFinder)); |
| } |
| buf.br(); |
| |
| // See whether we have an oop map at this PC |
| if (curMap != null) { |
| long curOffset = currentPc - startPc; |
| if (curOffset == curMapOffset) { |
| buf.append(genOopMapInfo(curMap)); |
| if (++curMapIndex >= maps.getSize()) { |
| curMap = null; |
| } else { |
| curMap = maps.getMapAt(curMapIndex); |
| if (curMap != null) { |
| curMapOffset = curMap.getOffset(); |
| } |
| } |
| } |
| } |
| } |
| |
| public void epilogue() { |
| } |
| }; |
| |
| disasm.decode(new CodeBlobVisitor()); |
| |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected String genInterpreterCodeletTitle(InterpreterCodelet codelet) { |
| Formatter buf = new Formatter(genHTML); |
| buf.append("Interpreter codelet ["); |
| buf.append(codelet.codeBegin().toString()); |
| buf.append(','); |
| buf.append(codelet.codeEnd().toString()); |
| buf.append(") - "); |
| buf.append(codelet.getDescription()); |
| return buf.toString(); |
| } |
| |
| protected String genInterpreterCodeletLinkPageHref(StubQueue stubq) { |
| return genBaseHref() + "interp_codelets"; |
| } |
| |
| public String genInterpreterCodeletLinksPage() { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue("Interpreter Codelets"); |
| buf.beginTag("ul"); |
| |
| Interpreter interp = VM.getVM().getInterpreter(); |
| StubQueue code = interp.getCode(); |
| InterpreterCodelet stub = (InterpreterCodelet) code.getFirst(); |
| while (stub != null) { |
| buf.beginTag("li"); |
| sun.jvm.hotspot.debugger.Address addr = stub.codeBegin(); |
| buf.link(genPCHref(addressToLong(addr)), stub.getDescription() + " @" + addr); |
| buf.endTag("li"); |
| stub = (InterpreterCodelet) code.getNext(stub); |
| } |
| |
| buf.endTag("ul"); |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } |
| |
| public String genHTML(InterpreterCodelet codelet) { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(genInterpreterCodeletTitle(codelet)); |
| Interpreter interp = VM.getVM().getInterpreter(); |
| StubQueue stubq = interp.getCode(); |
| |
| if (genHTML) { |
| buf.beginTag("h3"); |
| buf.link(genInterpreterCodeletLinkPageHref(stubq), "View links for all codelets"); |
| buf.endTag("h3"); |
| buf.br(); |
| } |
| |
| Stub prev = stubq.getPrev(codelet); |
| if (prev != null) { |
| if (genHTML) { |
| buf.beginTag("h3"); |
| buf.link(genPCHref(addressToLong(prev.codeBegin())), "View Previous Codelet"); |
| buf.endTag("h3"); |
| buf.br(); |
| } else { |
| buf.h3("Previous Codelet = 0x" + Long.toHexString(addressToLong(prev.codeBegin()))); |
| } |
| } |
| |
| buf.h3("Code"); |
| long stubStartPc = addressToLong(codelet.codeBegin()); |
| long stubEndPc = addressToLong(codelet.codeEnd()); |
| int range = (int) (stubEndPc - stubStartPc); |
| byte[] stubCode = readBuffer(codelet.codeBegin(), range); |
| Disassembler disasm = createDisassembler(stubStartPc, stubCode); |
| disasm.decode(new RawCodeVisitor(buf)); |
| |
| |
| Stub next = stubq.getNext(codelet); |
| if (next != null) { |
| if (genHTML) { |
| buf.beginTag("h3"); |
| buf.link(genPCHref(addressToLong(next.codeBegin())), "View Next Codelet"); |
| buf.endTag("h3"); |
| } else { |
| buf.h3("Next Codelet = 0x" + Long.toHexString(addressToLong(next.codeBegin()))); |
| } |
| } |
| |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } |
| |
| protected String genDumpKlassesTitle(InstanceKlass[] klasses) { |
| return (klasses.length == 1) ? "Create .class for this class" |
| : "Create .class for all classes"; |
| } |
| |
| protected String genDumpKlassesHref(InstanceKlass[] klasses) { |
| StringBuffer buf = new StringBuffer(genBaseHref()); |
| buf.append("jcore_multiple="); |
| for (int k = 0; k < klasses.length; k++) { |
| buf.append(klasses[k].getHandle().toString()); |
| buf.append(','); |
| } |
| return buf.toString(); |
| } |
| |
| protected String genDumpKlassesLink(InstanceKlass[] klasses) { |
| if (!genHTML) return ""; |
| |
| Formatter buf = new Formatter(genHTML); |
| buf.link(genDumpKlassesHref(klasses), genDumpKlassesTitle(klasses)); |
| return buf.toString(); |
| } |
| |
| public String genHTMLForKlassNames(InstanceKlass[] klasses) { |
| try { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(); |
| buf.h3(genDumpKlassesLink(klasses)); |
| |
| buf.append(genHTMLListForKlassNames(klasses)); |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected String genHTMLListForKlassNames(InstanceKlass[] klasses) { |
| final Formatter buf = new Formatter(genHTML); |
| buf.beginTable(0); |
| for (int i = 0; i < klasses.length; i++) { |
| InstanceKlass ik = klasses[i]; |
| buf.beginTag("tr"); |
| buf.cell(genKlassLink(ik)); |
| buf.endTag("tr"); |
| } |
| |
| buf.endTable(); |
| return buf.toString(); |
| } |
| |
| public String genHTMLForMethodNames(InstanceKlass klass) { |
| try { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(); |
| buf.append(genHTMLListForMethods(klass)); |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected String genHTMLListForMethods(InstanceKlass klass) { |
| Formatter buf = new Formatter(genHTML); |
| ObjArray methods = klass.getMethods(); |
| int numMethods = (int) methods.getLength(); |
| if (numMethods != 0) { |
| buf.h3("Methods"); |
| buf.beginTag("ul"); |
| for (int m = 0; m < numMethods; m++) { |
| Method mtd = (Method) methods.getObjAt(m); |
| buf.li(genMethodLink(mtd) + ";"); |
| } |
| buf.endTag("ul"); |
| } |
| return buf.toString(); |
| } |
| |
| protected String genHTMLListForInterfaces(InstanceKlass klass) { |
| try { |
| Formatter buf = new Formatter(genHTML); |
| ObjArray interfaces = klass.getLocalInterfaces(); |
| int numInterfaces = (int) interfaces.getLength(); |
| if (numInterfaces != 0) { |
| buf.h3("Interfaces"); |
| buf.beginTag("ul"); |
| for (int i = 0; i < numInterfaces; i++) { |
| InstanceKlass inf = (InstanceKlass) interfaces.getObjAt(i); |
| buf.li(genKlassLink(inf)); |
| } |
| buf.endTag("ul"); |
| } |
| return buf.toString(); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected String genFieldModifierString(AccessFlags acc) { |
| Formatter buf = new Formatter(genHTML); |
| if (acc.isPrivate()) { |
| buf.append("private "); |
| } else if (acc.isProtected()) { |
| buf.append("protected "); |
| } else if (acc.isPublic()) { |
| buf.append("public "); |
| } |
| |
| if (acc.isStatic()) { |
| buf.append("static "); |
| } |
| |
| if (acc.isFinal()) { |
| buf.append("final "); |
| } |
| if (acc.isVolatile()) { |
| buf.append("volatile "); |
| } |
| if (acc.isTransient()) { |
| buf.append("transient "); |
| } |
| |
| // javac generated flags |
| if (acc.isSynthetic()) { |
| buf.append("[synthetic] "); |
| } |
| return buf.toString(); |
| } |
| |
| public String genHTMLForFieldNames(InstanceKlass klass) { |
| try { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(); |
| buf.append(genHTMLListForFields(klass)); |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } catch (Exception exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected String genHTMLListForFields(InstanceKlass klass) { |
| Formatter buf = new Formatter(genHTML); |
| TypeArray fields = klass.getFields(); |
| int numFields = (int) fields.getLength(); |
| ConstantPool cp = klass.getConstants(); |
| if (numFields != 0) { |
| buf.h3("Fields"); |
| buf.beginList(); |
| for (int f = 0; f < numFields; f += InstanceKlass.NEXT_OFFSET) { |
| int nameIndex = fields.getShortAt(f + InstanceKlass.NAME_INDEX_OFFSET); |
| int sigIndex = fields.getShortAt(f + InstanceKlass.SIGNATURE_INDEX_OFFSET); |
| int genSigIndex = fields.getShortAt(f + InstanceKlass.GENERIC_SIGNATURE_INDEX_OFFSET); |
| Symbol f_name = cp.getSymbolAt(nameIndex); |
| Symbol f_sig = cp.getSymbolAt(sigIndex); |
| Symbol f_genSig = (genSigIndex != 0)? cp.getSymbolAt(genSigIndex) : null; |
| AccessFlags acc = new AccessFlags(fields.getShortAt(f + InstanceKlass.ACCESS_FLAGS_OFFSET)); |
| |
| buf.beginTag("li"); |
| buf.append(genFieldModifierString(acc)); |
| buf.append(' '); |
| Formatter sigBuf = new Formatter(genHTML); |
| new SignatureConverter(f_sig, sigBuf.getBuffer()).dispatchField(); |
| buf.append(sigBuf.toString().replace('/', '.')); |
| buf.append(' '); |
| buf.append(f_name.asString()); |
| buf.append(';'); |
| // is it generic? |
| if (f_genSig != null) { |
| buf.append(" [signature "); |
| buf.append(escapeHTMLSpecialChars(f_genSig.asString())); |
| buf.append("] "); |
| } |
| buf.endTag("li"); |
| } |
| buf.endList(); |
| } |
| return buf.toString(); |
| } |
| |
| protected String genKlassHierarchyHref(InstanceKlass klass) { |
| return genBaseHref() + "hierarchy=" + klass.getHandle(); |
| } |
| |
| protected String genKlassHierarchyTitle(InstanceKlass klass) { |
| Formatter buf = new Formatter(genHTML); |
| buf.append("Class Hierarchy of "); |
| buf.append(genKlassTitle(klass)); |
| return buf.toString(); |
| } |
| |
| protected String genKlassHierarchyLink(InstanceKlass klass) { |
| Formatter buf = new Formatter(genHTML); |
| buf.link(genKlassHierarchyHref(klass), genKlassHierarchyTitle(klass)); |
| return buf.toString(); |
| } |
| |
| protected String genHTMLListForSubKlasses(InstanceKlass klass) { |
| Formatter buf = new Formatter(genHTML); |
| Klass subklass = klass.getSubklassKlass(); |
| if (subklass != null) { |
| buf.beginList(); |
| while (subklass != null) { |
| if (subklass instanceof InstanceKlass) { |
| buf.li(genKlassLink((InstanceKlass)subklass)); |
| } |
| subklass = subklass.getNextSiblingKlass(); |
| } |
| buf.endList(); |
| } |
| return buf.toString(); |
| } |
| |
| public String genHTMLForKlassHierarchy(InstanceKlass klass) { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(genKlassHierarchyTitle(klass)); |
| |
| |
| buf.beginTag("pre"); |
| buf.append(genKlassLink(klass)); |
| buf.br(); |
| StringBuffer tabs = new StringBuffer(tab); |
| InstanceKlass superKlass = klass; |
| while ( (superKlass = (InstanceKlass) superKlass.getSuper()) != null ) { |
| buf.append(tabs); |
| buf.append(genKlassLink(superKlass)); |
| tabs.append(tab); |
| buf.br(); |
| } |
| buf.endTag("pre"); |
| |
| // generate subklass list |
| Klass subklass = klass.getSubklassKlass(); |
| if (subklass != null) { |
| buf.h3("Direct Subclasses"); |
| buf.append(genHTMLListForSubKlasses(klass)); |
| } |
| |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } |
| |
| protected String genDumpKlassHref(InstanceKlass klass) { |
| return genBaseHref() + "jcore=" + klass.getHandle(); |
| } |
| |
| protected String genDumpKlassLink(InstanceKlass klass) { |
| if (!genHTML) return ""; |
| |
| Formatter buf = new Formatter(genHTML); |
| buf.link(genDumpKlassHref(klass), "Create .class File"); |
| return buf.toString(); |
| } |
| |
| public String genHTML(InstanceKlass klass) { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(genKlassTitle(klass)); |
| InstanceKlass superKlass = (InstanceKlass) klass.getSuper(); |
| |
| if (genHTML) { |
| // super class tree and subclass list |
| buf.beginTag("h3"); |
| buf.link(genKlassHierarchyHref(klass), "View Class Hierarchy"); |
| buf.endTag("h3"); |
| } |
| |
| // jcore - create .class link |
| buf.h3(genDumpKlassLink(klass)); |
| |
| // super class |
| if (superKlass != null) { |
| buf.h3("Super Class"); |
| buf.append(genKlassLink(superKlass)); |
| } |
| |
| // interfaces |
| buf.append(genHTMLListForInterfaces(klass)); |
| |
| // fields |
| buf.append(genHTMLListForFields(klass)); |
| |
| // methods |
| buf.append(genHTMLListForMethods(klass)); |
| |
| // constant pool link |
| buf.h3("Constant Pool"); |
| buf.append(genConstantPoolLink(klass.getConstants())); |
| |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } |
| |
| protected sun.jvm.hotspot.debugger.Address parseAddress(String address) { |
| VM vm = VM.getVM(); |
| sun.jvm.hotspot.debugger.Address addr = vm.getDebugger().parseAddress(address); |
| return addr; |
| } |
| |
| protected long addressToLong(sun.jvm.hotspot.debugger.Address addr) { |
| return VM.getVM().getDebugger().getAddressValue(addr); |
| } |
| |
| protected sun.jvm.hotspot.debugger.Address longToAddress(long addr) { |
| return parseAddress("0x" + Long.toHexString(addr)); |
| } |
| |
| protected Oop getOopAtAddress(sun.jvm.hotspot.debugger.Address addr) { |
| OopHandle oopHandle = addr.addOffsetToAsOopHandle(0); |
| return VM.getVM().getObjectHeap().newOop(oopHandle); |
| } |
| |
| protected Oop getOopAtAddress(String address) { |
| sun.jvm.hotspot.debugger.Address addr = parseAddress(address); |
| return getOopAtAddress(addr); |
| } |
| |
| private void dumpKlass(InstanceKlass kls) throws IOException { |
| String klassName = kls.getName().asString(); |
| klassName = klassName.replace('/', File.separatorChar); |
| int index = klassName.lastIndexOf(File.separatorChar); |
| File dir = null; |
| if (index != -1) { |
| String dirName = klassName.substring(0, index); |
| dir = new File(DUMP_KLASS_OUTPUT_DIR, dirName); |
| } else { |
| dir = new File(DUMP_KLASS_OUTPUT_DIR); |
| } |
| |
| dir.mkdirs(); |
| File f = new File(dir, klassName.substring(klassName.lastIndexOf(File.separatorChar) + 1) |
| + ".class"); |
| f.createNewFile(); |
| FileOutputStream fis = new FileOutputStream(f); |
| ClassWriter cw = new ClassWriter(kls, fis); |
| cw.write(); |
| } |
| |
| public String genDumpKlass(InstanceKlass kls) { |
| try { |
| dumpKlass(kls); |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(genKlassTitle(kls)); |
| buf.append(".class created for "); |
| buf.append(genKlassLink(kls)); |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } catch(IOException exp) { |
| return genHTMLErrorMessage(exp); |
| } |
| } |
| |
| protected String genJavaStackTraceTitle(JavaThread thread) { |
| Formatter buf = new Formatter(genHTML); |
| buf.append("Java Stack Trace for "); |
| buf.append(thread.getThreadName()); |
| return buf.toString(); |
| } |
| |
| public String genHTMLForJavaStackTrace(JavaThread thread) { |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(genJavaStackTraceTitle(thread)); |
| |
| buf.append("Thread state = "); |
| buf.append(thread.getThreadState().toString()); |
| buf.br(); |
| buf.beginTag("pre"); |
| for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) { |
| Method method = vf.getMethod(); |
| buf.append(" - "); |
| buf.append(genMethodLink(method)); |
| buf.append(" @bci = " + vf.getBCI()); |
| |
| int lineNumber = method.getLineNumberFromBCI(vf.getBCI()); |
| if (lineNumber != -1) { |
| buf.append(", line = "); |
| buf.append(lineNumber); |
| } |
| |
| sun.jvm.hotspot.debugger.Address pc = vf.getFrame().getPC(); |
| if (pc != null) { |
| buf.append(", pc = "); |
| buf.link(genPCHref(addressToLong(pc)), pc.toString()); |
| } |
| |
| if (vf.isCompiledFrame()) { |
| buf.append(" (Compiled"); |
| } |
| else if (vf.isInterpretedFrame()) { |
| buf.append(" (Interpreted"); |
| } |
| |
| if (vf.mayBeImpreciseDbg()) { |
| buf.append("; information may be imprecise"); |
| } |
| buf.append(")"); |
| buf.br(); |
| } |
| |
| buf.endTag("pre"); |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } |
| |
| public String genHTMLForHyperlink(String href) { |
| if (href.startsWith("klass=")) { |
| href = href.substring(href.indexOf('=') + 1); |
| Oop obj = getOopAtAddress(href); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(obj instanceof InstanceKlass, "class= href with improper InstanceKlass!"); |
| } |
| return genHTML((InstanceKlass) obj); |
| } else if (href.startsWith("method=")) { |
| href = href.substring(href.indexOf('=') + 1); |
| Oop obj = getOopAtAddress(href); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(obj instanceof Method, "method= href with improper Method!"); |
| } |
| return genHTML((Method) obj); |
| } else if (href.startsWith("nmethod=")) { |
| String addr = href.substring(href.indexOf('=') + 1); |
| Object obj = VMObjectFactory.newObject(NMethod.class, parseAddress(addr)); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(obj instanceof NMethod, "nmethod= href with improper NMethod!"); |
| } |
| return genHTML((NMethod) obj); |
| } else if (href.startsWith("pc=")) { |
| String address = href.substring(href.indexOf('=') + 1); |
| return genHTML(parseAddress(address)); |
| } else if (href.startsWith("pc_multiple=")) { |
| int indexOfComma = href.indexOf(','); |
| if (indexOfComma == -1) { |
| String firstPC = href.substring(href.indexOf('=') + 1); |
| return genHTMLForRawDisassembly(parseAddress(firstPC), null); |
| } else { |
| String firstPC = href.substring(href.indexOf('=') + 1, indexOfComma); |
| return genHTMLForRawDisassembly(parseAddress(firstPC), href.substring(indexOfComma + 1)); |
| } |
| } else if (href.startsWith("interp_codelets")) { |
| return genInterpreterCodeletLinksPage(); |
| } else if (href.startsWith("hierarchy=")) { |
| href = href.substring(href.indexOf('=') + 1); |
| Oop obj = getOopAtAddress(href); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(obj instanceof InstanceKlass, "class= href with improper InstanceKlass!"); |
| } |
| return genHTMLForKlassHierarchy((InstanceKlass) obj); |
| } else if (href.startsWith("cpool=")) { |
| href = href.substring(href.indexOf('=') + 1); |
| Oop obj = getOopAtAddress(href); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(obj instanceof ConstantPool, "cpool= href with improper ConstantPool!"); |
| } |
| return genHTML((ConstantPool) obj); |
| } else if (href.startsWith("jcore=")) { |
| href = href.substring(href.indexOf('=') + 1); |
| Oop obj = getOopAtAddress(href); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(obj instanceof InstanceKlass, "jcore= href with improper InstanceKlass!"); |
| } |
| return genDumpKlass((InstanceKlass) obj); |
| } else if (href.startsWith("jcore_multiple=")) { |
| href = href.substring(href.indexOf('=') + 1); |
| Formatter buf = new Formatter(genHTML); |
| buf.genHTMLPrologue(); |
| StringTokenizer st = new StringTokenizer(href, ","); |
| while (st.hasMoreTokens()) { |
| Oop obj = getOopAtAddress(st.nextToken()); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(obj instanceof InstanceKlass, "jcore_multiple= href with improper InstanceKlass!"); |
| } |
| |
| InstanceKlass kls = (InstanceKlass) obj; |
| try { |
| dumpKlass(kls); |
| buf.append(".class created for "); |
| buf.append(genKlassLink(kls)); |
| } catch(Exception exp) { |
| buf.bold("can't .class for " + |
| genKlassTitle(kls) + |
| " : " + |
| exp.getMessage()); |
| } |
| buf.br(); |
| } |
| |
| buf.genHTMLEpilogue(); |
| return buf.toString(); |
| } else { |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(false, "unknown href link!"); |
| } |
| return null; |
| } |
| } |
| } |