diff agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java @ 0:a61af66fc99e jdk7-b24

Initial load
author duke
date Sat, 01 Dec 2007 00:00:00 +0000
parents
children ba764ed4b6f2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,2018 @@
+/*
+ * 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 = "&nbsp;&nbsp;";
+           tab = "&nbsp;&nbsp;&nbsp;&nbsp;";
+       } 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("&lt;");
+               break;
+            case '>':
+               buf.append("&gt;");
+               break;
+            case '&':
+               buf.append("&amp;");
+               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;
+      }
+   }
+}