1 /*
   2  * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.ui.classbrowser;
  26 
  27 import java.io.*;
  28 import java.util.*;
  29 import sun.jvm.hotspot.asm.*;
  30 import sun.jvm.hotspot.asm.sparc.*;
  31 import sun.jvm.hotspot.asm.x86.*;
  32 import sun.jvm.hotspot.asm.ia64.*;
  33 import sun.jvm.hotspot.asm.amd64.*;
  34 import sun.jvm.hotspot.code.*;
  35 import sun.jvm.hotspot.compiler.*;
  36 import sun.jvm.hotspot.debugger.*;
  37 import sun.jvm.hotspot.interpreter.*;
  38 import sun.jvm.hotspot.memory.*;
  39 import sun.jvm.hotspot.oops.*;
  40 import sun.jvm.hotspot.runtime.*;
  41 import sun.jvm.hotspot.tools.jcore.*;
  42 import sun.jvm.hotspot.types.*;
  43 import sun.jvm.hotspot.utilities.*;
  44 
  45 public class HTMLGenerator implements /* imports */ ClassConstants {
  46     static class Formatter {
  47         boolean html;
  48         StringBuffer buf = new StringBuffer();
  49 
  50         Formatter(boolean h) {
  51             html = h;
  52         }
  53 
  54         void append(String s) {
  55             buf.append(s);
  56         }
  57 
  58         void append(int s) {
  59             buf.append(s);
  60         }
  61 
  62         void append(char s) {
  63             buf.append(s);
  64         }
  65 
  66         void append(StringBuffer s) {
  67             buf.append(s);
  68         }
  69 
  70         void append(Formatter s) {
  71             buf.append(s);
  72         }
  73 
  74         StringBuffer getBuffer() {
  75             return buf;
  76         }
  77 
  78         public String toString() {
  79             return buf.toString();
  80         }
  81 
  82         void wrap(String tag, String text) {
  83             wrap(tag, tag, text);
  84         }
  85         void wrap(String before, String after, String text) {
  86             beginTag(before);
  87             append(text);
  88             endTag(after);
  89         }
  90 
  91         // header tags
  92         void h1(String s) { nl(); wrap("h1", s); nl(); }
  93         void h2(String s) { nl(); wrap("h2", s); nl(); }
  94         void h3(String s) { nl(); wrap("h3", s); nl(); }
  95         void h4(String s) { nl(); wrap("h4", s); nl(); }
  96 
  97         // list tags
  98         void beginList()  { beginTag("ul"); nl(); }
  99         void endList()    { endTag("ul"); nl();   }
 100         void beginListItem() { beginTag("li"); }
 101         void endListItem()   { endTag("li"); nl();   }
 102         void li(String s) { wrap("li", s); nl();  }
 103 
 104         // table tags
 105         void beginTable(int border) {
 106             beginTag("table border='" + border + "'");
 107         }
 108         void cell(String s) { wrap("td", s); }
 109         void headerCell(String s) { wrap("th", s); }
 110         void endTable() { endTag("table"); }
 111 
 112         void link(String href, String text) {
 113             wrap("a href='" + href + "'", "a", text);
 114         }
 115         void beginTag(String s) {
 116             if (html) { append("<"); append(s); append(">"); }
 117         }
 118         void endTag(String s) {
 119             if (html) {
 120                append("</"); append(s); append(">");
 121             } else {
 122                if (s.equals("table") || s.equals("tr")) {
 123                   nl();
 124                }
 125                if (s.equals("td") || s.equals("th")) {
 126                   append(" ");
 127                }
 128             }
 129         }
 130         void bold(String s) {
 131             wrap("b", s);
 132         }
 133 
 134         void nl() {
 135             if (!html) buf.append("\n");
 136         }
 137 
 138         void br() {
 139             if (html) append("<br>");
 140             else      append("\n");
 141         }
 142         void genEmptyHTML() {
 143             if (html) append("<html></html>");
 144         }
 145 
 146         void genHTMLPrologue() {
 147             if (html) append("<html><body>");
 148         }
 149 
 150         void genHTMLPrologue(String title) {
 151             if (html) {
 152                 append("<html><head><title>");
 153                 append(title);
 154                 append("</title></head>");
 155                 append("<body>");
 156             }
 157             h2(title);
 158         }
 159         void genHTMLEpilogue() {
 160             if (html) append("</body></html>");
 161         }
 162 
 163     }
 164 
 165    private static final String DUMP_KLASS_OUTPUT_DIR = ".";
 166    private static final int NATIVE_CODE_SIZE = 200;
 167    private final String spaces;
 168    private final String tab;
 169 
 170    private boolean genHTML = true;
 171 
 172    public HTMLGenerator() {
 173        this(true);
 174    }
 175 
 176    public HTMLGenerator(boolean html) {
 177        genHTML = html;
 178        if (html) {
 179            spaces = "&nbsp;&nbsp;";
 180            tab = "&nbsp;&nbsp;&nbsp;&nbsp;";
 181        } else {
 182            spaces = "  ";
 183            tab = "    ";
 184        }
 185     }
 186 
 187    private static CPUHelper cpuHelper;
 188    static {
 189       VM.registerVMInitializedObserver(new Observer() {
 190          public void update(Observable o, Object data) {
 191             initialize();
 192          }
 193       });
 194    }
 195 
 196    private static synchronized void initialize() {
 197       String cpu = VM.getVM().getCPU();
 198       if (cpu.equals("sparc")) {
 199          cpuHelper = new SPARCHelper();
 200       } else if (cpu.equals("x86")) {
 201          cpuHelper = new X86Helper();
 202       } else if (cpu.equals("amd64") || cpu.equals("x86_64")) {
 203          cpuHelper = new AMD64Helper();
 204       } else if (cpu.equals("ia64")) {
 205          cpuHelper = new IA64Helper();
 206       } else {
 207           throw new RuntimeException("cpu '" + cpu + "' is not yet supported!");
 208       }
 209    }
 210 
 211    protected static synchronized CPUHelper getCPUHelper() {
 212       return cpuHelper;
 213    }
 214 
 215    protected String escapeHTMLSpecialChars(String value) {
 216       if (!genHTML) return value;
 217 
 218       Formatter buf = new Formatter(genHTML);
 219       int len = value.length();
 220       for (int i=0; i < len; i++) {
 221          char c = value.charAt(i);
 222          switch (c) {
 223             case '<':
 224                buf.append("&lt;");
 225                break;
 226             case '>':
 227                buf.append("&gt;");
 228                break;
 229             case '&':
 230                buf.append("&amp;");
 231                break;
 232             default:
 233                buf.append(c);
 234                break;
 235          }
 236       }
 237       return buf.toString();
 238    }
 239 
 240    public String genHTMLForMessage(String message) {
 241       Formatter buf = new Formatter(genHTML);
 242       buf.genHTMLPrologue(message);
 243       buf.genHTMLEpilogue();
 244       return buf.toString();
 245    }
 246 
 247    public String genHTMLErrorMessage(Exception exp) {
 248       exp.printStackTrace();
 249       return genHTMLForMessage(exp.getClass().getName() + " : " + exp.getMessage());
 250    }
 251 
 252    public String genHTMLForWait(String message) {
 253       Formatter buf = new Formatter(genHTML);
 254       buf.genHTMLPrologue("Please wait ..");
 255       buf.h2(message);
 256       return buf.toString();
 257    }
 258 
 259    protected String genKlassTitle(InstanceKlass klass) {
 260       Formatter buf = new Formatter(genHTML);
 261       AccessFlags acc = klass.getAccessFlagsObj();
 262       if (acc.isPublic()) {
 263          buf.append("public ");
 264       } else if (acc.isProtected()) {
 265          buf.append("protected ");
 266       } else if (acc.isPrivate()) {
 267          buf.append("private ");
 268       }
 269 
 270       if (acc.isStatic()) {
 271          buf.append("static ");
 272       }
 273 
 274       if (acc.isAbstract() ) {
 275          buf.append("abstract ");
 276       } else if (acc.isFinal()) {
 277          buf.append("final ");
 278       }
 279 
 280       if (acc.isStrict()) {
 281          buf.append("strict ");
 282       }
 283 
 284       // javac generated flags
 285       if (acc.isEnum()) {
 286          buf.append("[enum] ");
 287       }
 288       if (acc.isSynthetic()) {
 289          buf.append("[synthetic] ");
 290       }
 291 
 292       if (klass.isInterface()) {
 293          buf.append("interface");
 294       } else {
 295          buf.append("class");
 296       }
 297 
 298       buf.append(' ');
 299       buf.append(klass.getName().asString().replace('/', '.'));
 300       // is it generic?
 301       Symbol genSig = klass.getGenericSignature();
 302       if (genSig != null) {
 303          buf.append(" [signature ");
 304          buf.append(escapeHTMLSpecialChars(genSig.asString()));
 305          buf.append("] ");
 306       } else {
 307          buf.append(' ');
 308       }
 309       buf.append('@');
 310       buf.append(klass.getHandle().toString());
 311       return buf.toString();
 312    }
 313 
 314    protected String genBaseHref() {
 315       return "";
 316    }
 317 
 318    protected String genKlassHref(InstanceKlass klass) {
 319       return genBaseHref() + "klass=" + klass.getHandle();
 320    }
 321 
 322    protected String genKlassLink(InstanceKlass klass) {
 323       Formatter buf = new Formatter(genHTML);
 324       buf.link(genKlassHref(klass), genKlassTitle(klass));
 325       return buf.toString();
 326    }
 327 
 328    protected String genMethodModifierString(AccessFlags acc) {
 329       Formatter buf = new Formatter(genHTML);
 330       if (acc.isPrivate()) {
 331          buf.append("private ");
 332       } else if (acc.isProtected()) {
 333          buf.append("protected ");
 334       } else if (acc.isPublic()) {
 335          buf.append("public ");
 336       }
 337 
 338       if (acc.isStatic()) {
 339          buf.append("static ");
 340       } else if (acc.isAbstract() ) {
 341          buf.append("abstract ");
 342       } else if (acc.isFinal()) {
 343          buf.append("final ");
 344       }
 345 
 346        if (acc.isNative()) {
 347          buf.append("native ");
 348       }
 349 
 350       if (acc.isStrict()) {
 351          buf.append("strict ");
 352       }
 353 
 354       if (acc.isSynchronized()) {
 355          buf.append("synchronized ");
 356       }
 357 
 358       // javac generated flags
 359       if (acc.isBridge()) {
 360          buf.append("[bridge] ");
 361       }
 362 
 363       if (acc.isSynthetic()) {
 364          buf.append("[synthetic] ");
 365       }
 366 
 367       if (acc.isVarArgs()) {
 368          buf.append("[varargs] ");
 369       }
 370 
 371       return buf.toString();
 372    }
 373 
 374    protected String genMethodNameAndSignature(Method method) {
 375       Formatter buf = new Formatter(genHTML);
 376       buf.append(genMethodModifierString(method.getAccessFlagsObj()));
 377       Symbol sig = method.getSignature();
 378       new SignatureConverter(sig, buf.getBuffer()).iterateReturntype();
 379       buf.append(" ");
 380       String methodName = method.getName().asString();
 381       buf.append(escapeHTMLSpecialChars(methodName));
 382       buf.append('(');
 383       new SignatureConverter(sig, buf.getBuffer()).iterateParameters();
 384       buf.append(')');
 385       // is it generic?
 386       Symbol genSig = method.getGenericSignature();
 387       if (genSig != null) {
 388          buf.append(" [signature ");
 389          buf.append(escapeHTMLSpecialChars(genSig.asString()));
 390          buf.append("] ");
 391       }
 392       return buf.toString().replace('/', '.');
 393    }
 394 
 395    protected String genMethodTitle(Method method) {
 396       Formatter buf = new Formatter(genHTML);
 397       buf.append(genMethodNameAndSignature(method));
 398       buf.append(' ');
 399       buf.append('@');
 400       buf.append(method.getHandle().toString());
 401       return buf.toString();
 402    }
 403 
 404    protected String genMethodHref(Method m) {
 405       return genBaseHref() + "method=" + m.getHandle();
 406    }
 407 
 408    protected String genMethodLink(Method m) {
 409       Formatter buf = new Formatter(genHTML);
 410       buf.link(genMethodHref(m), genMethodTitle(m));
 411       return buf.toString();
 412    }
 413 
 414    protected String genMethodAndKlassLink(Method m) {
 415       Formatter buf = new Formatter(genHTML);
 416       buf.append(genMethodLink(m));
 417       buf.append(" of ");
 418       buf.append(genKlassLink((InstanceKlass) m.getMethodHolder()));
 419       return buf.toString();
 420    }
 421 
 422    protected String genNMethodHref(NMethod nm) {
 423       return genBaseHref() + "nmethod=" + nm.getAddress();
 424    }
 425 
 426    public String genNMethodTitle(NMethod nmethod) {
 427       Formatter buf = new Formatter(genHTML);
 428       Method m = nmethod.getMethod();
 429 
 430       buf.append("Disassembly for compiled method [");
 431       buf.append(genMethodTitle(m));
 432       buf.append(" ] ");
 433       buf.append('@');
 434       buf.append(nmethod.getAddress().toString());
 435       return buf.toString();
 436    }
 437 
 438    protected String genNMethodLink(NMethod nm) {
 439       Formatter buf = new Formatter(genHTML);
 440       buf.link(genNMethodHref(nm), genNMethodTitle(nm));
 441       return buf.toString();
 442    }
 443 
 444    public String genCodeBlobTitle(CodeBlob blob) {
 445       Formatter buf = new Formatter(genHTML);
 446       buf.append("Disassembly for code blob " + blob.getName() + " [");
 447       buf.append(blob.getClass().getName());
 448       buf.append(" ] @");
 449       buf.append(blob.getAddress().toString());
 450       return buf.toString();
 451    }
 452 
 453    protected BytecodeDisassembler createBytecodeDisassembler(Method m) {
 454       return new BytecodeDisassembler(m);
 455    }
 456 
 457    private String genLowHighShort(int val) {
 458       Formatter buf = new Formatter(genHTML);
 459       buf.append('#');
 460       buf.append(Integer.toString(val & 0xFFFF));
 461       buf.append(" #");
 462       buf.append(Integer.toString((val >> 16) & 0xFFFF));
 463       return buf.toString();
 464    }
 465 
 466    private String genListOfShort(short[] values) {
 467       if (values == null || values.length == 0)  return "";
 468       Formatter buf = new Formatter(genHTML);
 469       buf.append('[');
 470       for (int i = 0; i < values.length; i++) {
 471           if (i > 0)  buf.append(' ');
 472           buf.append('#');
 473           buf.append(Integer.toString(values[i]));
 474       }
 475       buf.append(']');
 476       return buf.toString();
 477    }
 478 
 479    protected String genHTMLTableForConstantPool(ConstantPool cpool) {
 480       Formatter buf = new Formatter(genHTML);
 481       buf.beginTable(1);
 482 
 483       buf.beginTag("tr");
 484       buf.headerCell("Index");
 485       buf.headerCell("Constant Type");
 486       buf.headerCell("Constant Value");
 487       buf.endTag("tr");
 488 
 489       final int length = (int) cpool.getLength();
 490       // zero'th pool entry is always invalid. ignore it.
 491       for (int index = 1; index < length; index++) {
 492          buf.beginTag("tr");
 493          buf.cell(Integer.toString(index));
 494 
 495          int ctag = (int) cpool.getTags().getByteAt((int) index);
 496          switch (ctag) {
 497             case JVM_CONSTANT_Integer:
 498                buf.cell("JVM_CONSTANT_Integer");
 499                buf.cell(Integer.toString(cpool.getIntAt(index)));
 500                break;
 501 
 502             case JVM_CONSTANT_Float:
 503                buf.cell("JVM_CONSTANT_Float");
 504                buf.cell(Float.toString(cpool.getFloatAt(index)));
 505                break;
 506 
 507             case JVM_CONSTANT_Long:
 508                buf.cell("JVM_CONSTANT_Long");
 509                buf.cell(Long.toString(cpool.getLongAt(index)));
 510                // long entries occupy two slots
 511                index++;
 512                break;
 513 
 514             case JVM_CONSTANT_Double:
 515                buf.cell("JVM_CONSTANT_Double");
 516                buf.cell(Double.toString(cpool.getDoubleAt(index)));
 517                // double entries occupy two slots
 518                index++;
 519                break;
 520 
 521             case JVM_CONSTANT_UnresolvedClass:
 522                buf.cell("JVM_CONSTANT_UnresolvedClass");
 523                buf.cell(cpool.getSymbolAt(index).asString());
 524                break;
 525 
 526             case JVM_CONSTANT_UnresolvedClassInError:
 527                buf.cell("JVM_CONSTANT_UnresolvedClassInError");
 528                buf.cell(cpool.getSymbolAt(index).asString());
 529                break;
 530 
 531             case JVM_CONSTANT_Class:
 532                buf.cell("JVM_CONSTANT_Class");
 533                Klass klass = (Klass) cpool.getObjAtRaw(index);
 534                if (klass instanceof InstanceKlass) {
 535                   buf.cell(genKlassLink((InstanceKlass) klass));
 536                } else {
 537                   buf.cell(klass.getName().asString().replace('/', '.'));
 538                }
 539                break;
 540 
 541             case JVM_CONSTANT_UnresolvedString:
 542                buf.cell("JVM_CONSTANT_UnresolvedString");
 543                buf.cell("\"" +
 544                  escapeHTMLSpecialChars(cpool.getSymbolAt(index).asString()) +
 545                  "\"");
 546                break;
 547 
 548             case JVM_CONSTANT_Utf8:
 549                buf.cell("JVM_CONSTANT_Utf8");
 550                buf.cell("\"" +
 551                  escapeHTMLSpecialChars(cpool.getSymbolAt(index).asString()) +
 552                  "\"");
 553                break;
 554 
 555             case JVM_CONSTANT_String:
 556                buf.cell("JVM_CONSTANT_String");
 557                buf.cell("\"" +
 558                  escapeHTMLSpecialChars(OopUtilities.stringOopToString(cpool.getObjAtRaw(index))) + "\"");
 559                break;
 560 
 561             case JVM_CONSTANT_Fieldref:
 562                buf.cell("JVM_CONSTANT_Fieldref");
 563                buf.cell(genLowHighShort(cpool.getIntAt(index)));
 564                break;
 565 
 566             case JVM_CONSTANT_Methodref:
 567                buf.cell("JVM_CONSTANT_Methodref");
 568                buf.cell(genLowHighShort(cpool.getIntAt(index)));
 569                break;
 570 
 571             case JVM_CONSTANT_InterfaceMethodref:
 572                buf.cell("JVM_CONSTANT_InterfaceMethodref");
 573                buf.cell(genLowHighShort(cpool.getIntAt(index)));
 574                break;
 575 
 576             case JVM_CONSTANT_NameAndType:
 577                buf.cell("JVM_CONSTANT_NameAndType");
 578                buf.cell(genLowHighShort(cpool.getIntAt(index)));
 579                break;
 580 
 581             case JVM_CONSTANT_ClassIndex:
 582                buf.cell("JVM_CONSTANT_ClassIndex");
 583                buf.cell(Integer.toString(cpool.getIntAt(index)));
 584                break;
 585 
 586             case JVM_CONSTANT_StringIndex:
 587                buf.cell("JVM_CONSTANT_StringIndex");
 588                buf.cell(Integer.toString(cpool.getIntAt(index)));
 589                break;
 590 
 591             case JVM_CONSTANT_MethodHandle:
 592                buf.cell("JVM_CONSTANT_MethodHandle");
 593                buf.cell(genLowHighShort(cpool.getIntAt(index)));
 594                break;
 595 
 596             case JVM_CONSTANT_MethodType:
 597                buf.cell("JVM_CONSTANT_MethodType");
 598                buf.cell(Integer.toString(cpool.getIntAt(index)));
 599                break;
 600 
 601             case JVM_CONSTANT_InvokeDynamic:
 602                buf.cell("JVM_CONSTANT_InvokeDynamic");
 603                buf.cell(genLowHighShort(cpool.getIntAt(index)) +
 604                         genListOfShort(cpool.getBootstrapSpecifierAt(index)));
 605                break;
 606 
 607             default:
 608                throw new InternalError("unknown tag: " + ctag);
 609          }
 610 
 611          buf.endTag("tr");
 612       }
 613 
 614       buf.endTable();
 615       return buf.toString();
 616    }
 617 
 618    public String genHTML(ConstantPool cpool) {
 619       try {
 620          Formatter buf = new Formatter(genHTML);
 621          buf.genHTMLPrologue(genConstantPoolTitle(cpool));
 622          buf.h3("Holder Class");
 623          buf.append(genKlassLink((InstanceKlass) cpool.getPoolHolder()));
 624          buf.h3("Constants");
 625          buf.append(genHTMLTableForConstantPool(cpool));
 626          buf.genHTMLEpilogue();
 627          return buf.toString();
 628       } catch (Exception exp) {
 629          return genHTMLErrorMessage(exp);
 630       }
 631    }
 632 
 633    protected String genConstantPoolHref(ConstantPool cpool) {
 634       return genBaseHref() + "cpool=" + cpool.getHandle();
 635    }
 636 
 637    protected String genConstantPoolTitle(ConstantPool cpool) {
 638       Formatter buf = new Formatter(genHTML);
 639       buf.append("Constant Pool of [");
 640       buf.append(genKlassTitle((InstanceKlass) cpool.getPoolHolder()));
 641       buf.append("] @");
 642       buf.append(cpool.getHandle().toString());
 643       return buf.toString();
 644    }
 645 
 646    protected String genConstantPoolLink(ConstantPool cpool) {
 647       Formatter buf = new Formatter(genHTML);
 648       buf.link(genConstantPoolHref(cpool), genConstantPoolTitle(cpool));
 649       return buf.toString();
 650    }
 651 
 652    public String genHTML(Method method) {
 653       try {
 654          final Formatter buf = new Formatter(genHTML);
 655          buf.genHTMLPrologue(genMethodTitle(method));
 656 
 657          buf.h3("Holder Class");
 658          buf.append(genKlassLink((InstanceKlass) method.getMethodHolder()));
 659 
 660          NMethod nmethod = method.getNativeMethod();
 661          if (nmethod != null) {
 662             buf.h3("Compiled Code");
 663             buf.append(genNMethodLink(nmethod));
 664          }
 665 
 666          boolean hasThrows = method.hasCheckedExceptions();
 667          ConstantPool cpool = ((InstanceKlass) method.getMethodHolder()).getConstants();
 668          if (hasThrows) {
 669             buf.h3("Checked Exception(s)");
 670             CheckedExceptionElement[] exceptions = method.getCheckedExceptions();
 671             buf.beginTag("ul");
 672             for (int exp = 0; exp < exceptions.length; exp++) {
 673                short cpIndex = (short) exceptions[exp].getClassCPIndex();
 674                ConstantPool.CPSlot obj = cpool.getSlotAt(cpIndex);
 675                if (obj.isMetaData()) {
 676                  buf.li((obj.getSymbol()).asString().replace('/', '.'));
 677                } else {
 678                  buf.li(genKlassLink((InstanceKlass)obj.getOop()));
 679                }
 680             }
 681             buf.endTag("ul");
 682          }
 683 
 684          if (method.isNative() || method.isAbstract()) {
 685            buf.genHTMLEpilogue();
 686            return buf.toString();
 687          }
 688 
 689          buf.h3("Bytecode");
 690          BytecodeDisassembler disasm = createBytecodeDisassembler(method);
 691          final boolean hasLineNumbers = method.hasLineNumberTable();
 692          disasm.decode(new BytecodeVisitor() {
 693                           private Method method;
 694                           public void prologue(Method m) {
 695                              method = m;
 696                              buf.beginTable(0);
 697                              buf.beginTag("tr");
 698                              if (hasLineNumbers) {
 699                                 buf.headerCell("line");
 700                              }
 701                              buf.headerCell("bci" + spaces);
 702                              buf.headerCell("bytecode");
 703                              buf.endTag("tr");
 704                           }
 705 
 706                           public void visit(Bytecode instr) {
 707                              int curBci = instr.bci();
 708                              buf.beginTag("tr");
 709                              if (hasLineNumbers) {
 710                                 int lineNumber = method.getLineNumberFromBCI(curBci);
 711                                 buf.cell(Integer.toString(lineNumber) + spaces);
 712                              }
 713                              buf.cell(Integer.toString(curBci) + spaces);
 714 
 715                              buf.beginTag("td");
 716                              String instrStr = null;
 717                              try {
 718                                  instrStr = escapeHTMLSpecialChars(instr.toString());
 719                              } catch (RuntimeException re) {
 720                                  buf.append("exception during bytecode processing");
 721                                  buf.endTag("td");
 722                                  buf.endTag("tr");
 723                                  re.printStackTrace();
 724                                  return;
 725                              }
 726 
 727                              if (instr instanceof BytecodeNew) {
 728                                 BytecodeNew newBytecode = (BytecodeNew) instr;
 729                                 InstanceKlass klass = newBytecode.getNewKlass();
 730                                 if (klass != null) {
 731                                     buf.link(genKlassHref(klass), instrStr);
 732                                 } else {
 733                                     buf.append(instrStr);
 734                                 }
 735                              } else if(instr instanceof BytecodeInvoke) {
 736                                 BytecodeInvoke invokeBytecode = (BytecodeInvoke) instr;
 737                                 Method m = invokeBytecode.getInvokedMethod();
 738                                 if (m != null) {
 739                                    buf.link(genMethodHref(m), instrStr);
 740                                    buf.append(" of ");
 741                                    InstanceKlass klass = (InstanceKlass) m.getMethodHolder();
 742                                    buf.link(genKlassHref(klass), genKlassTitle(klass));
 743                                 } else {
 744                                    buf.append(instrStr);
 745                                 }
 746                              } else if (instr instanceof BytecodeGetPut) {
 747                                 BytecodeGetPut getPut = (BytecodeGetPut) instr;
 748                                 sun.jvm.hotspot.oops.Field f = getPut.getField();
 749                                 buf.append(instrStr);
 750                                 if (f != null) {
 751                                    InstanceKlass klass = f.getFieldHolder();
 752                                    buf.append(" of ");
 753                                    buf.link(genKlassHref(klass), genKlassTitle(klass));
 754                                 }
 755                              } else if (instr instanceof BytecodeLoadConstant) {
 756                                 BytecodeLoadConstant ldc = (BytecodeLoadConstant) instr;
 757                                 if (ldc.isKlassConstant()) {
 758                                    Object oop = ldc.getKlass();
 759                                    if (oop instanceof Klass) {
 760                                       buf.append("<a href='");
 761                                       buf.append(genKlassHref((InstanceKlass) oop));
 762                                       buf.append("'>");
 763                                       buf.append(instrStr);
 764                                       buf.append("</a>");
 765                                    } else {
 766                                       // unresolved klass literal
 767                                       buf.append(instrStr);
 768                                    }
 769                                 } else {
 770                                    // not a klass literal
 771                                    buf.append(instrStr);
 772                                 }
 773                              } else {
 774                                 buf.append(instrStr);
 775                              }
 776                              buf.endTag("td");
 777                              buf.endTag("tr");
 778                           }
 779 
 780                           public void epilogue() {
 781                              buf.endTable();
 782                           }
 783                        });
 784 
 785          // display exception table for this method
 786          TypeArray exceptionTable = method.getExceptionTable();
 787          // exception table is 4 tuple array of shorts
 788          int numEntries = (int)exceptionTable.getLength() / 4;
 789          if (numEntries != 0) {
 790             buf.h4("Exception Table");
 791             buf.beginTable(1);
 792             buf.beginTag("tr");
 793             buf.headerCell("start bci");
 794             buf.headerCell("end bci");
 795             buf.headerCell("handler bci");
 796             buf.headerCell("catch type");
 797             buf.endTag("tr");
 798 
 799             for (int e = 0; e < numEntries; e += 4) {
 800                buf.beginTag("tr");
 801                buf.cell(Integer.toString(exceptionTable.getIntAt(e)));
 802                buf.cell(Integer.toString(exceptionTable.getIntAt(e + 1)));
 803                buf.cell(Integer.toString(exceptionTable.getIntAt(e + 2)));
 804                short cpIndex = (short) exceptionTable.getIntAt(e + 3);
 805                ConstantPool.CPSlot obj = cpIndex == 0? null : cpool.getSlotAt(cpIndex);
 806                if (obj == null) {
 807                   buf.cell("Any");
 808                } else if (obj.isMetaData()) {
 809                  buf.cell(obj.getSymbol().asString().replace('/', '.'));
 810                } else {
 811                  buf.cell(genKlassLink((InstanceKlass)obj.getOop()));
 812                }
 813                buf.endTag("tr");
 814             }
 815 
 816             buf.endTable();
 817          }
 818 
 819          // display constant pool hyperlink
 820          buf.h3("Constant Pool");
 821          buf.append(genConstantPoolLink(cpool));
 822          buf.genHTMLEpilogue();
 823          return buf.toString();
 824       } catch (Exception exp) {
 825          return genHTMLErrorMessage(exp);
 826       }
 827    }
 828 
 829    protected Disassembler createDisassembler(long startPc, byte[] code) {
 830       return getCPUHelper().createDisassembler(startPc, code);
 831    }
 832 
 833    protected SymbolFinder createSymbolFinder() {
 834       return new DummySymbolFinder();
 835    }
 836 
 837    // genHTML for a given address. Address may be a PC or
 838    // methodOop or klassOop.
 839 
 840    public String genHTMLForAddress(String addrStr) {
 841       return genHTML(parseAddress(addrStr));
 842    }
 843 
 844    public String genHTML(sun.jvm.hotspot.debugger.Address pc) {
 845       CodeBlob blob = null;
 846 
 847       try {
 848          blob = (CodeBlob)VM.getVM().getCodeCache().findBlobUnsafe(pc);
 849       } catch (Exception exp) {
 850          // ignore
 851       }
 852 
 853       if (blob != null) {
 854          if (blob instanceof NMethod) {
 855             return genHTML((NMethod)blob);
 856          } else {
 857             // may be interpreter code.
 858             Interpreter interp = VM.getVM().getInterpreter();
 859             if (interp.contains(pc)) {
 860                InterpreterCodelet codelet = interp.getCodeletContaining(pc);
 861                if (codelet == null) {
 862                   return "Unknown location in the Interpreter: " + pc;
 863                }
 864                return genHTML(codelet);
 865             }
 866             return genHTML(blob);
 867          }
 868       } else if (VM.getVM().getCodeCache().contains(pc)) {
 869          return "Unknown location in the CodeCache: " + pc;
 870       }
 871 
 872       // did not find nmethod.
 873       // try methodOop, klassOop and constantPoolOop.
 874       try {
 875          Oop obj = getOopAtAddress(pc);
 876          if (obj != null) {
 877             if (obj instanceof Method) {
 878                return genHTML((Method) obj);
 879             } else if (obj instanceof InstanceKlass) {
 880                return genHTML((InstanceKlass) obj);
 881             } else if (obj instanceof ConstantPool) {
 882                return genHTML((ConstantPool) obj);
 883             }
 884          }
 885       } catch (Exception exp) {
 886          // ignore
 887       }
 888 
 889       // didn't find any. do raw disassembly.
 890       return genHTMLForRawDisassembly(pc, null);
 891    }
 892 
 893    protected byte[] readBuffer(sun.jvm.hotspot.debugger.Address addr, int size) {
 894       byte[] buf = new byte[size];
 895       for (int b = 0; b < size; b++) {
 896          buf[b] = (byte) addr.getJByteAt(b);
 897       }
 898       return buf;
 899    }
 900 
 901     public String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address startPc, int size) {
 902       try {
 903          return genHTMLForRawDisassembly(startPc, null, readBuffer(startPc, size));
 904       } catch (Exception exp) {
 905          return genHTMLErrorMessage(exp);
 906       }
 907    }
 908 
 909    protected String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address startPc,
 910                                              String prevPCs) {
 911       try {
 912          return genHTMLForRawDisassembly(startPc, prevPCs, readBuffer(startPc, NATIVE_CODE_SIZE));
 913       } catch (Exception exp) {
 914          return genHTMLErrorMessage(exp);
 915       }
 916    }
 917 
 918    protected String genPCHref(long targetPc) {
 919       return genBaseHref() + "pc=0x" + Long.toHexString(targetPc);
 920    }
 921 
 922    protected String genMultPCHref(String pcs) {
 923       StringBuffer buf = new StringBuffer(genBaseHref());
 924       buf.append("pc_multiple=");
 925       buf.append(pcs);
 926       return buf.toString();
 927    }
 928 
 929    protected String genPCHref(long currentPc, sun.jvm.hotspot.asm.Address addr) {
 930       String href = null;
 931       if (addr instanceof PCRelativeAddress) {
 932          PCRelativeAddress pcRelAddr = (PCRelativeAddress) addr;
 933          href = genPCHref(currentPc + pcRelAddr.getDisplacement());
 934       } else if(addr instanceof DirectAddress) {
 935          href =  genPCHref(((DirectAddress) addr).getValue());
 936       }
 937 
 938       return href;
 939    }
 940 
 941    class RawCodeVisitor implements InstructionVisitor {
 942       private int instrSize = 0;
 943       private Formatter buf;
 944       private SymbolFinder symFinder = createSymbolFinder();
 945 
 946       RawCodeVisitor(Formatter buf) {
 947          this.buf = buf;
 948       }
 949 
 950       public int getInstructionSize() {
 951          return  instrSize;
 952       }
 953 
 954       public void prologue() {
 955       }
 956 
 957       public void visit(long currentPc, Instruction instr) {
 958          String href = null;
 959           if (instr.isCall()) {
 960              CallInstruction call = (CallInstruction) instr;
 961              sun.jvm.hotspot.asm.Address addr = call.getBranchDestination();
 962              href = genPCHref(currentPc, addr);
 963           }
 964 
 965           instrSize += instr.getSize();
 966           buf.append("0x");
 967           buf.append(Long.toHexString(currentPc));
 968           buf.append(':');
 969           buf.append(tab);
 970 
 971           if (href != null) {
 972              buf.link(href, instr.asString(currentPc, symFinder));
 973           } else {
 974              buf.append(instr.asString(currentPc, symFinder));
 975           }
 976           buf.br();
 977       }
 978 
 979       public void epilogue() {
 980       }
 981    };
 982 
 983    protected String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address addr,
 984                                              String prevPCs,
 985                                              byte[] code) {
 986       try {
 987          long startPc = addressToLong(addr);
 988          Disassembler disasm = createDisassembler(startPc, code);
 989          final Formatter buf = new Formatter(genHTML);
 990          buf.genHTMLPrologue("Disassembly @0x" + Long.toHexString(startPc));
 991 
 992          if (prevPCs != null && genHTML) {
 993              buf.beginTag("p");
 994              buf.link(genMultPCHref(prevPCs), "show previous code ..");
 995              buf.endTag("p");
 996          }
 997 
 998 
 999          buf.h3("Code");
1000          RawCodeVisitor visitor = new RawCodeVisitor(buf);
1001          disasm.decode(visitor);
1002 
1003          if (genHTML) buf.beginTag("p");
1004          Formatter tmpBuf = new Formatter(genHTML);
1005          tmpBuf.append("0x");
1006          tmpBuf.append(Long.toHexString(startPc + visitor.getInstructionSize()).toString());
1007          tmpBuf.append(",0x");
1008          tmpBuf.append(Long.toHexString(startPc));
1009          if (prevPCs != null) {
1010             tmpBuf.append(',');
1011             tmpBuf.append(prevPCs);
1012          }
1013          if (genHTML) {
1014              buf.link(genMultPCHref(tmpBuf.toString()), "show more code ..");
1015              buf.endTag("p");
1016          }
1017 
1018          buf.genHTMLEpilogue();
1019          return buf.toString();
1020       } catch (Exception exp) {
1021          return genHTMLErrorMessage(exp);
1022       }
1023    }
1024 
1025    protected String genSafepointInfo(NMethod nm, PCDesc pcDesc) {
1026        ScopeDesc sd = nm.getScopeDescAt(pcDesc.getRealPC(nm));
1027        Formatter buf = new Formatter(genHTML);
1028        Formatter tabs = new Formatter(genHTML);
1029        tabs.append(tab + tab + tab); // Initial indent for debug info
1030 
1031        buf.beginTag("pre");
1032        genScope(buf, tabs, sd);
1033 
1034        // Reset indent for scalar replaced objects
1035        tabs = new Formatter(genHTML);
1036        tabs.append(tab + tab + tab); // Initial indent for debug info
1037 
1038        genScObjInfo(buf, tabs, sd);
1039        buf.endTag("pre");
1040 
1041        buf.append(genOopMapInfo(nm, pcDesc));
1042 
1043        return buf.toString();
1044    }
1045 
1046     protected void genScope(Formatter buf, Formatter tabs, ScopeDesc sd) {
1047         if (sd == null) {
1048             return;
1049         }
1050 
1051         genScope(buf, tabs, sd.sender());
1052 
1053         buf.append(tabs);
1054         Method m = sd.getMethod();
1055         buf.append(genMethodAndKlassLink(m));
1056         int bci = sd.getBCI();
1057         buf.append(" @ bci = ");
1058         buf.append(Integer.toString(bci));
1059 
1060         int line = m.getLineNumberFromBCI(bci);
1061         if (line != -1) {
1062             buf.append(", line = ");
1063             buf.append(Integer.toString(line));
1064         }
1065 
1066         List locals = sd.getLocals();
1067         if (locals != null) {
1068             buf.br();
1069             buf.append(tabs);
1070             buf.append(genHTMLForLocals(sd, locals));
1071         }
1072 
1073         List expressions = sd.getExpressions();
1074         if (expressions != null) {
1075             buf.br();
1076             buf.append(tabs);
1077             buf.append(genHTMLForExpressions(sd, expressions));
1078         }
1079 
1080         List monitors = sd.getMonitors();
1081         if (monitors != null) {
1082             buf.br();
1083             buf.append(tabs);
1084             buf.append(genHTMLForMonitors(sd, monitors));
1085         }
1086 
1087         buf.br();
1088         tabs.append(tab);
1089     }
1090 
1091     protected void genScObjInfo(Formatter buf, Formatter tabs, ScopeDesc sd) {
1092         if (sd == null) {
1093             return;
1094         }
1095 
1096         List objects = sd.getObjects();
1097         if (objects == null) {
1098             return;
1099         }
1100         int length = objects.size();
1101         for (int i = 0; i < length; i++) {
1102             buf.append(tabs);
1103             ObjectValue ov = (ObjectValue)objects.get(i);
1104             buf.append("ScObj" + i);
1105             ScopeValue sv = ov.getKlass();
1106             if (Assert.ASSERTS_ENABLED) {
1107                 Assert.that(sv.isConstantOop(), "scalar replaced object klass must be constant oop");
1108             }
1109             ConstantOopReadValue klv = (ConstantOopReadValue)sv;
1110             OopHandle klHandle = klv.getValue();
1111             if (Assert.ASSERTS_ENABLED) {
1112                 Assert.that(klHandle != null, "scalar replaced object klass must be not NULL");
1113             }
1114             Oop obj = VM.getVM().getObjectHeap().newOop(klHandle);
1115             if (obj instanceof InstanceKlass) {
1116                 InstanceKlass kls = (InstanceKlass) obj;
1117                 buf.append(" " + kls.getName().asString() + "={");
1118                 int flen = ov.fieldsSize();
1119                 int klen = kls.getJavaFieldsCount();
1120                 int findex = 0;
1121                 for (int index = 0; index < klen; index++) {
1122                     int accsFlags = kls.getFieldAccessFlags(index);
1123                     Symbol f_name = kls.getFieldName(index);
1124                     AccessFlags access = new AccessFlags(accsFlags);
1125                     if (!access.isStatic()) {
1126                         ScopeValue svf = ov.getFieldAt(findex++);
1127                         String    fstr = scopeValueAsString(sd, svf);
1128                         buf.append(" [" + f_name.asString() + " :"+ index + "]=(#" + fstr + ")");
1129                     }
1130                 }
1131                 buf.append(" }");
1132             } else {
1133                 buf.append(" ");
1134                 int flen = ov.fieldsSize();
1135                 if (obj instanceof TypeArrayKlass) {
1136                     TypeArrayKlass kls = (TypeArrayKlass) obj;
1137                     buf.append(kls.getElementTypeName() + "[" + flen + "]");
1138                 } else if (obj instanceof ObjArrayKlass) {
1139                     ObjArrayKlass kls = (ObjArrayKlass) obj;
1140                     Klass elobj = kls.getBottomKlass();
1141                     if (elobj instanceof InstanceKlass) {
1142                         buf.append(elobj.getName().asString());
1143                     } else if (elobj instanceof TypeArrayKlass) {
1144                         TypeArrayKlass elkls = (TypeArrayKlass) elobj;
1145                         buf.append(elkls.getElementTypeName());
1146                     } else {
1147                         if (Assert.ASSERTS_ENABLED) {
1148                             Assert.that(false, "unknown scalar replaced object klass!");
1149                         }
1150                     }
1151                     buf.append("[" + flen + "]");
1152                     int ndim = (int) kls.getDimension();
1153                     while (--ndim > 0) {
1154                         buf.append("[]");
1155                     }
1156                 } else {
1157                     if (Assert.ASSERTS_ENABLED) {
1158                         Assert.that(false, "unknown scalar replaced object klass!");
1159                     }
1160                 }
1161                 buf.append("={");
1162                 for (int findex = 0; findex < flen; findex++) {
1163                     ScopeValue svf = ov.getFieldAt(findex);
1164                     String fstr = scopeValueAsString(sd, svf);
1165                     buf.append(" [" + findex + "]=(#" + fstr + ")");
1166                 }
1167                 buf.append(" }");
1168             }
1169             buf.br();
1170         }
1171     }
1172 
1173    protected String genHTMLForOopMap(OopMap map) {
1174       final int stack0 = VMRegImpl.getStack0().getValue();
1175       Formatter buf = new Formatter(genHTML);
1176 
1177       final class OopMapValueIterator {
1178          final Formatter iterate(OopMapStream oms, String type, boolean printContentReg) {
1179             Formatter tmpBuf = new Formatter(genHTML);
1180             boolean found = false;
1181             tmpBuf.beginTag("tr");
1182             tmpBuf.beginTag("td");
1183             tmpBuf.append(type);
1184             for (; ! oms.isDone(); oms.next()) {
1185                OopMapValue omv = oms.getCurrent();
1186                if (omv == null) {
1187                   continue;
1188                }
1189                found = true;
1190                VMReg vmReg = omv.getReg();
1191                int reg = vmReg.getValue();
1192                if (reg < stack0) {
1193                   tmpBuf.append(VMRegImpl.getRegisterName(reg));
1194                } else {
1195                   tmpBuf.append('[');
1196                   tmpBuf.append(Integer.toString((reg - stack0) * 4));
1197                   tmpBuf.append(']');
1198                }
1199                if (printContentReg) {
1200                   tmpBuf.append(" = ");
1201                   VMReg vmContentReg = omv.getContentReg();
1202                   int contentReg = vmContentReg.getValue();
1203                   if (contentReg < stack0) {
1204                      tmpBuf.append(VMRegImpl.getRegisterName(contentReg));
1205                   } else {
1206                      tmpBuf.append('[');
1207                      tmpBuf.append(Integer.toString((contentReg - stack0) * 4));
1208                      tmpBuf.append(']');
1209                   }
1210                }
1211                tmpBuf.append(spaces);
1212             }
1213             tmpBuf.endTag("td");
1214             tmpBuf.endTag("tr");
1215             return found ? tmpBuf : new Formatter(genHTML);
1216          }
1217       }
1218 
1219       buf.beginTable(0);
1220 
1221       OopMapValueIterator omvIterator = new OopMapValueIterator();
1222       OopMapStream oms = new OopMapStream(map, OopMapValue.OopTypes.OOP_VALUE);
1223       buf.append(omvIterator.iterate(oms, "Oops:", false));
1224 
1225       oms = new OopMapStream(map, OopMapValue.OopTypes.NARROWOOP_VALUE);
1226       buf.append(omvIterator.iterate(oms, "narrowOops:", false));
1227 
1228       oms = new OopMapStream(map, OopMapValue.OopTypes.VALUE_VALUE);
1229       buf.append(omvIterator.iterate(oms, "Values:", false));
1230 
1231       oms = new OopMapStream(map, OopMapValue.OopTypes.CALLEE_SAVED_VALUE);
1232       buf.append(omvIterator.iterate(oms, "Callee saved:",  true));
1233 
1234       oms = new OopMapStream(map, OopMapValue.OopTypes.DERIVED_OOP_VALUE);
1235       buf.append(omvIterator.iterate(oms, "Derived oops:", true));
1236 
1237       buf.endTag("table");
1238       return buf.toString();
1239    }
1240 
1241 
1242    protected String genOopMapInfo(NMethod nmethod, PCDesc pcDesc) {
1243       OopMapSet mapSet = nmethod.getOopMaps();
1244       if (mapSet == null || (mapSet.getSize() <= 0))
1245         return "";
1246       int pcOffset = pcDesc.getPCOffset();
1247       OopMap map = mapSet.findMapAtOffset(pcOffset, VM.getVM().isDebugging());
1248       if (map == null) {
1249          throw new IllegalArgumentException("no oopmap at safepoint!");
1250       }
1251 
1252       return genOopMapInfo(map);
1253    }
1254 
1255    protected String genOopMapInfo(OopMap map) {
1256      Formatter buf = new Formatter(genHTML);
1257      buf.beginTag("pre");
1258      buf.append("OopMap: ");
1259      buf.br();
1260      buf.append(genHTMLForOopMap(map));
1261      buf.endTag("pre");
1262 
1263      return buf.toString();
1264    }
1265 
1266    protected String locationAsString(Location loc) {
1267       Formatter buf = new Formatter(genHTML);
1268       if (loc.isIllegal()) {
1269          buf.append("illegal");
1270       } else {
1271          Location.Where  w  = loc.getWhere();
1272          Location.Type type = loc.getType();
1273 
1274          if (w == Location.Where.ON_STACK) {
1275             buf.append("stack[" + loc.getStackOffset() + "]");
1276          } else if (w == Location.Where.IN_REGISTER) {
1277             boolean isFloat = (type == Location.Type.FLOAT_IN_DBL ||
1278                                type == Location.Type.DBL);
1279             int regNum = loc.getRegisterNumber();
1280             VMReg vmReg = new VMReg(regNum);
1281             buf.append(VMRegImpl.getRegisterName(vmReg.getValue()));
1282          }
1283 
1284          buf.append(", ");
1285          if (type == Location.Type.NORMAL) {
1286             buf.append("normal");
1287          } else if (type == Location.Type.OOP) {
1288             buf.append("oop");
1289          } else if (type == Location.Type.NARROWOOP) {
1290             buf.append("narrowoop");
1291          } else if (type == Location.Type.INT_IN_LONG) {
1292             buf.append("int");
1293          } else if (type == Location.Type.LNG) {
1294             buf.append("long");
1295          } else if (type == Location.Type.FLOAT_IN_DBL) {
1296             buf.append("float");
1297          } else if (type == Location.Type.DBL) {
1298             buf.append("double");
1299          } else if (type == Location.Type.ADDR) {
1300             buf.append("address");
1301          } else if (type == Location.Type.INVALID) {
1302             buf.append("invalid");
1303          }
1304       }
1305       return buf.toString();
1306    }
1307 
1308    private String scopeValueAsString(ScopeDesc sd, ScopeValue sv) {
1309       Formatter buf = new Formatter(genHTML);
1310       if (sv.isConstantInt()) {
1311          buf.append("int ");
1312          ConstantIntValue intValue = (ConstantIntValue) sv;
1313          buf.append(Integer.toString(intValue.getValue()));
1314       } else if (sv.isConstantLong()) {
1315          buf.append("long ");
1316          ConstantLongValue longValue = (ConstantLongValue) sv;
1317          buf.append(Long.toString(longValue.getValue()));
1318          buf.append("L");
1319       } else if (sv.isConstantDouble()) {
1320          buf.append("double ");
1321          ConstantDoubleValue dblValue = (ConstantDoubleValue) sv;
1322          buf.append(Double.toString(dblValue.getValue()));
1323          buf.append("D");
1324       } else if (sv.isConstantOop()) {
1325          buf.append("oop ");
1326          ConstantOopReadValue oopValue = (ConstantOopReadValue) sv;
1327          OopHandle oopHandle = oopValue.getValue();
1328          if (oopHandle != null) {
1329             buf.append(oopHandle.toString());
1330          } else {
1331             buf.append("null");
1332          }
1333       } else if (sv.isLocation()) {
1334          LocationValue lvalue = (LocationValue) sv;
1335          Location loc = lvalue.getLocation();
1336          if (loc != null) {
1337             buf.append(locationAsString(loc));
1338          } else {
1339             buf.append("null");
1340          }
1341       } else if (sv.isObject()) {
1342          ObjectValue ov = (ObjectValue)sv;
1343          buf.append("#ScObj" + sd.getObjects().indexOf(ov));
1344       } else {
1345          buf.append("unknown scope value " + sv);
1346       }
1347       return buf.toString();
1348    }
1349 
1350    protected String genHTMLForScopeValues(ScopeDesc sd, boolean locals, List values) {
1351       int length = values.size();
1352       Formatter buf = new Formatter(genHTML);
1353       buf.append(locals? "locals " : "expressions ");
1354       for (int i = 0; i < length; i++) {
1355          ScopeValue sv = (ScopeValue) values.get(i);
1356          if (sv == null) {
1357             continue;
1358          }
1359          buf.append('(');
1360          if (locals) {
1361             Symbol name = sd.getMethod().getLocalVariableName(sd.getBCI(), i);
1362             if (name != null) {
1363                buf.append("'");
1364                buf.append(name.asString());
1365                buf.append('\'');
1366             } else {
1367                buf.append("[");
1368                buf.append(Integer.toString(i));
1369                buf.append(']');
1370             }
1371          } else {
1372             buf.append("[");
1373             buf.append(Integer.toString(i));
1374             buf.append(']');
1375          }
1376 
1377          buf.append(", ");
1378          buf.append(scopeValueAsString(sd, sv));
1379          buf.append(") ");
1380       }
1381 
1382       return buf.toString();
1383    }
1384 
1385    protected String genHTMLForLocals(ScopeDesc sd, List locals) {
1386       return genHTMLForScopeValues(sd, true, locals);
1387    }
1388 
1389    protected String genHTMLForExpressions(ScopeDesc sd, List expressions) {
1390       return genHTMLForScopeValues(sd, false, expressions);
1391    }
1392 
1393    protected String genHTMLForMonitors(ScopeDesc sd, List monitors) {
1394       int length = monitors.size();
1395       Formatter buf = new Formatter(genHTML);
1396       buf.append("monitors ");
1397       for (int i = 0; i < length; i++) {
1398          MonitorValue mv = (MonitorValue) monitors.get(i);
1399          if (mv == null) {
1400             continue;
1401          }
1402          buf.append("(owner = ");
1403          ScopeValue owner = mv.owner();
1404          if (owner != null) {
1405             buf.append(scopeValueAsString(sd, owner));
1406          } else {
1407             buf.append("null");
1408          }
1409          buf.append(", lock = ");
1410 
1411          Location loc = mv.basicLock();
1412          if (loc != null) {
1413             buf.append(locationAsString(loc));
1414          } else {
1415             buf.append("null");
1416          }
1417          buf.append(") ");
1418       }
1419       return buf.toString();
1420    }
1421 
1422    public String genHTML(final NMethod nmethod) {
1423       try {
1424          final Formatter buf = new Formatter(genHTML);
1425          buf.genHTMLPrologue(genNMethodTitle(nmethod));
1426          buf.h3("Method");
1427          buf.append(genMethodAndKlassLink(nmethod.getMethod()));
1428 
1429          buf.h3("Compiled Code");
1430          sun.jvm.hotspot.debugger.Address instsBegin = nmethod.instsBegin();
1431          sun.jvm.hotspot.debugger.Address instsEnd   = nmethod.instsEnd();
1432          final int instsSize = nmethod.instsSize();
1433          final long startPc = addressToLong(instsBegin);
1434          final byte[] code = new byte[instsSize];
1435          for (int i=0; i < code.length; i++)
1436             code[i] = instsBegin.getJByteAt(i);
1437 
1438          final long verifiedEntryPoint = addressToLong(nmethod.getVerifiedEntryPoint());
1439          final long entryPoint = addressToLong(nmethod.getEntryPoint());
1440          final Map safepoints = nmethod.getSafepoints();
1441 
1442          final SymbolFinder symFinder = createSymbolFinder();
1443          final Disassembler disasm = createDisassembler(startPc, code);
1444          class NMethodVisitor implements InstructionVisitor {
1445             public void prologue() {
1446             }
1447 
1448             public void visit(long currentPc, Instruction instr) {
1449                String href = null;
1450                if (instr.isCall()) {
1451                   CallInstruction call = (CallInstruction) instr;
1452                   sun.jvm.hotspot.asm.Address addr = call.getBranchDestination();
1453                   href = genPCHref(currentPc, addr);
1454                }
1455 
1456                if (currentPc == verifiedEntryPoint) {
1457                    buf.bold("Verified Entry Point"); buf.br();
1458                }
1459                if (currentPc == entryPoint) {
1460                    buf.bold(">Entry Point"); buf.br();
1461                }
1462 
1463                PCDesc pcDesc = (PCDesc) safepoints.get(longToAddress(currentPc));
1464 
1465                if (pcDesc != null) {
1466                   buf.append(genSafepointInfo(nmethod, pcDesc));
1467                }
1468 
1469                buf.append("0x");
1470                buf.append(Long.toHexString(currentPc));
1471                buf.append(':');
1472                buf.append(tab);
1473 
1474                if (href != null) {
1475                   buf.link(href, instr.asString(currentPc, symFinder));
1476                } else {
1477                   buf.append(instr.asString(currentPc, symFinder));
1478                }
1479 
1480                buf.br();
1481             }
1482 
1483             public void epilogue() {
1484             }
1485          };
1486 
1487          disasm.decode(new NMethodVisitor());
1488 
1489          sun.jvm.hotspot.debugger.Address stubBegin = nmethod.stubBegin();
1490          if (stubBegin != null) {
1491             sun.jvm.hotspot.debugger.Address stubEnd   = nmethod.stubEnd();
1492             buf.h3("Stub");
1493             long stubStartPc = addressToLong(stubBegin);
1494             long stubEndPc = addressToLong(stubEnd);
1495             int range = (int) (stubEndPc - stubStartPc);
1496             byte[] stubCode = readBuffer(stubBegin, range);
1497             Disassembler disasm2 = createDisassembler(stubStartPc, stubCode);
1498             disasm2.decode(new NMethodVisitor());
1499          }
1500          buf.genHTMLEpilogue();
1501          return buf.toString();
1502       } catch (Exception exp) {
1503          return genHTMLErrorMessage(exp);
1504       }
1505    }
1506 
1507   public String genHTML(final CodeBlob blob) {
1508       try {
1509          final Formatter buf = new Formatter(genHTML);
1510          buf.genHTMLPrologue(genCodeBlobTitle(blob));
1511          buf.h3("CodeBlob");
1512 
1513          buf.h3("Compiled Code");
1514          final sun.jvm.hotspot.debugger.Address codeBegin = blob.codeBegin();
1515          final int codeSize = blob.getCodeSize();
1516          final long startPc = addressToLong(codeBegin);
1517          final byte[] code = new byte[codeSize];
1518          for (int i=0; i < code.length; i++)
1519             code[i] = codeBegin.getJByteAt(i);
1520 
1521          final SymbolFinder symFinder = createSymbolFinder();
1522          final Disassembler disasm = createDisassembler(startPc, code);
1523          class CodeBlobVisitor implements InstructionVisitor {
1524             OopMapSet maps;
1525             OopMap curMap;
1526             int curMapIndex;
1527             long curMapOffset;
1528             public void prologue() {
1529               maps = blob.getOopMaps();
1530               if (maps != null && (maps.getSize() > 0)) {
1531                 curMap = maps.getMapAt(0);
1532                 if (curMap != null) {
1533                   curMapOffset = curMap.getOffset();
1534                 }
1535               }
1536             }
1537 
1538             public void visit(long currentPc, Instruction instr) {
1539                String href = null;
1540                if (instr.isCall()) {
1541                   CallInstruction call = (CallInstruction) instr;
1542                   sun.jvm.hotspot.asm.Address addr = call.getBranchDestination();
1543                   href = genPCHref(currentPc, addr);
1544                }
1545 
1546                buf.append("0x");
1547                buf.append(Long.toHexString(currentPc));
1548                buf.append(':');
1549                buf.append(tab);
1550 
1551                if (href != null) {
1552                   buf.link(href, instr.asString(currentPc, symFinder));
1553                } else {
1554                    buf.append(instr.asString(currentPc, symFinder));
1555                }
1556                buf.br();
1557 
1558                // See whether we have an oop map at this PC
1559                if (curMap != null) {
1560                  long curOffset = currentPc - startPc;
1561                  if (curOffset == curMapOffset) {
1562                    buf.append(genOopMapInfo(curMap));
1563                    if (++curMapIndex >= maps.getSize()) {
1564                      curMap = null;
1565                    } else {
1566                      curMap = maps.getMapAt(curMapIndex);
1567                      if (curMap != null) {
1568                        curMapOffset = curMap.getOffset();
1569                      }
1570                    }
1571                  }
1572                }
1573             }
1574 
1575             public void epilogue() {
1576             }
1577          };
1578 
1579          disasm.decode(new CodeBlobVisitor());
1580 
1581          buf.genHTMLEpilogue();
1582          return buf.toString();
1583       } catch (Exception exp) {
1584          return genHTMLErrorMessage(exp);
1585       }
1586    }
1587 
1588    protected String genInterpreterCodeletTitle(InterpreterCodelet codelet) {
1589       Formatter buf = new Formatter(genHTML);
1590       buf.append("Interpreter codelet [");
1591       buf.append(codelet.codeBegin().toString());
1592       buf.append(',');
1593       buf.append(codelet.codeEnd().toString());
1594       buf.append(") - ");
1595       buf.append(codelet.getDescription());
1596       return buf.toString();
1597    }
1598 
1599    protected String genInterpreterCodeletLinkPageHref(StubQueue stubq) {
1600       return genBaseHref() + "interp_codelets";
1601    }
1602 
1603    public String genInterpreterCodeletLinksPage() {
1604       Formatter buf = new Formatter(genHTML);
1605       buf.genHTMLPrologue("Interpreter Codelets");
1606       buf.beginTag("ul");
1607 
1608       Interpreter interp = VM.getVM().getInterpreter();
1609       StubQueue code = interp.getCode();
1610       InterpreterCodelet stub = (InterpreterCodelet) code.getFirst();
1611       while (stub != null) {
1612          buf.beginTag("li");
1613          sun.jvm.hotspot.debugger.Address addr = stub.codeBegin();
1614          buf.link(genPCHref(addressToLong(addr)), stub.getDescription() + " @" + addr);
1615          buf.endTag("li");
1616          stub = (InterpreterCodelet) code.getNext(stub);
1617       }
1618 
1619       buf.endTag("ul");
1620       buf.genHTMLEpilogue();
1621       return buf.toString();
1622    }
1623 
1624    public String genHTML(InterpreterCodelet codelet) {
1625       Formatter buf = new Formatter(genHTML);
1626       buf.genHTMLPrologue(genInterpreterCodeletTitle(codelet));
1627       Interpreter interp = VM.getVM().getInterpreter();
1628       StubQueue stubq = interp.getCode();
1629 
1630       if (genHTML) {
1631          buf.beginTag("h3");
1632          buf.link(genInterpreterCodeletLinkPageHref(stubq), "View links for all codelets");
1633          buf.endTag("h3");
1634          buf.br();
1635       }
1636 
1637       Stub prev = stubq.getPrev(codelet);
1638       if (prev != null) {
1639          if (genHTML) {
1640             buf.beginTag("h3");
1641             buf.link(genPCHref(addressToLong(prev.codeBegin())), "View Previous Codelet");
1642             buf.endTag("h3");
1643             buf.br();
1644          } else {
1645             buf.h3("Previous Codelet = 0x" + Long.toHexString(addressToLong(prev.codeBegin())));
1646          }
1647       }
1648 
1649       buf.h3("Code");
1650       long stubStartPc = addressToLong(codelet.codeBegin());
1651       long stubEndPc = addressToLong(codelet.codeEnd());
1652       int range = (int) (stubEndPc - stubStartPc);
1653       byte[] stubCode = readBuffer(codelet.codeBegin(), range);
1654       Disassembler disasm = createDisassembler(stubStartPc, stubCode);
1655       disasm.decode(new RawCodeVisitor(buf));
1656 
1657 
1658       Stub next = stubq.getNext(codelet);
1659       if (next != null) {
1660          if (genHTML) {
1661             buf.beginTag("h3");
1662             buf.link(genPCHref(addressToLong(next.codeBegin())), "View Next Codelet");
1663             buf.endTag("h3");
1664          } else {
1665             buf.h3("Next Codelet = 0x" + Long.toHexString(addressToLong(next.codeBegin())));
1666          }
1667       }
1668 
1669       buf.genHTMLEpilogue();
1670       return buf.toString();
1671    }
1672 
1673    protected String genDumpKlassesTitle(InstanceKlass[] klasses) {
1674       return (klasses.length == 1) ? "Create .class for this class"
1675                                    : "Create .class for all classes";
1676    }
1677 
1678    protected String genDumpKlassesHref(InstanceKlass[] klasses) {
1679       StringBuffer buf = new StringBuffer(genBaseHref());
1680       buf.append("jcore_multiple=");
1681       for (int k = 0; k < klasses.length; k++) {
1682          buf.append(klasses[k].getHandle().toString());
1683          buf.append(',');
1684       }
1685       return buf.toString();
1686    }
1687 
1688    protected String genDumpKlassesLink(InstanceKlass[] klasses) {
1689       if (!genHTML) return "";
1690 
1691       Formatter buf = new Formatter(genHTML);
1692       buf.link(genDumpKlassesHref(klasses), genDumpKlassesTitle(klasses));
1693       return buf.toString();
1694    }
1695 
1696    public String genHTMLForKlassNames(InstanceKlass[] klasses) {
1697       try {
1698          Formatter buf = new Formatter(genHTML);
1699          buf.genHTMLPrologue();
1700          buf.h3(genDumpKlassesLink(klasses));
1701 
1702          buf.append(genHTMLListForKlassNames(klasses));
1703          buf.genHTMLEpilogue();
1704          return buf.toString();
1705       } catch (Exception exp) {
1706          return genHTMLErrorMessage(exp);
1707       }
1708    }
1709 
1710    protected String genHTMLListForKlassNames(InstanceKlass[] klasses) {
1711       final Formatter buf = new Formatter(genHTML);
1712       buf.beginTable(0);
1713       for (int i = 0; i < klasses.length; i++) {
1714          InstanceKlass ik = klasses[i];
1715          buf.beginTag("tr");
1716          buf.cell(genKlassLink(ik));
1717          buf.endTag("tr");
1718       }
1719 
1720       buf.endTable();
1721       return buf.toString();
1722    }
1723 
1724    public String genHTMLForMethodNames(InstanceKlass klass) {
1725       try {
1726          Formatter buf = new Formatter(genHTML);
1727          buf.genHTMLPrologue();
1728          buf.append(genHTMLListForMethods(klass));
1729          buf.genHTMLEpilogue();
1730          return buf.toString();
1731       } catch (Exception exp) {
1732          return genHTMLErrorMessage(exp);
1733       }
1734    }
1735 
1736    protected String genHTMLListForMethods(InstanceKlass klass) {
1737       Formatter buf = new Formatter(genHTML);
1738       ObjArray methods = klass.getMethods();
1739       int numMethods = (int) methods.getLength();
1740       if (numMethods != 0) {
1741          buf.h3("Methods");
1742          buf.beginTag("ul");
1743          for (int m = 0; m < numMethods; m++) {
1744             Method mtd = (Method) methods.getObjAt(m);
1745             buf.li(genMethodLink(mtd) + ";");
1746          }
1747          buf.endTag("ul");
1748       }
1749       return buf.toString();
1750    }
1751 
1752    protected String genHTMLListForInterfaces(InstanceKlass klass) {
1753       try {
1754          Formatter buf = new Formatter(genHTML);
1755          ObjArray interfaces = klass.getLocalInterfaces();
1756          int numInterfaces = (int) interfaces.getLength();
1757          if (numInterfaces != 0) {
1758             buf.h3("Interfaces");
1759             buf.beginTag("ul");
1760             for (int i = 0; i < numInterfaces; i++) {
1761                InstanceKlass inf = (InstanceKlass) interfaces.getObjAt(i);
1762                buf.li(genKlassLink(inf));
1763             }
1764             buf.endTag("ul");
1765          }
1766          return buf.toString();
1767       } catch (Exception exp) {
1768          return genHTMLErrorMessage(exp);
1769       }
1770    }
1771 
1772    protected String genFieldModifierString(AccessFlags acc) {
1773       Formatter buf = new Formatter(genHTML);
1774       if (acc.isPrivate()) {
1775          buf.append("private ");
1776       } else if (acc.isProtected()) {
1777          buf.append("protected ");
1778       } else if (acc.isPublic()) {
1779          buf.append("public ");
1780       }
1781 
1782       if (acc.isStatic()) {
1783          buf.append("static ");
1784       }
1785 
1786       if (acc.isFinal()) {
1787          buf.append("final ");
1788       }
1789       if (acc.isVolatile()) {
1790          buf.append("volatile ");
1791       }
1792       if (acc.isTransient()) {
1793          buf.append("transient ");
1794       }
1795 
1796       // javac generated flags
1797       if (acc.isSynthetic()) {
1798          buf.append("[synthetic] ");
1799       }
1800       return buf.toString();
1801    }
1802 
1803    public String genHTMLForFieldNames(InstanceKlass klass) {
1804       try {
1805          Formatter buf = new Formatter(genHTML);
1806          buf.genHTMLPrologue();
1807          buf.append(genHTMLListForFields(klass));
1808          buf.genHTMLEpilogue();
1809          return buf.toString();
1810       } catch (Exception exp) {
1811          return genHTMLErrorMessage(exp);
1812       }
1813    }
1814 
1815    protected String genHTMLListForFields(InstanceKlass klass) {
1816       Formatter buf = new Formatter(genHTML);
1817       int numFields = klass.getJavaFieldsCount();
1818       if (numFields != 0) {
1819          buf.h3("Fields");
1820          buf.beginList();
1821          for (int f = 0; f < numFields; f++) {
1822            sun.jvm.hotspot.oops.Field field = klass.getFieldByIndex(f);
1823            String f_name = ((NamedFieldIdentifier)field.getID()).getName();
1824            Symbol f_sig  = field.getSignature();
1825            Symbol f_genSig = field.getGenericSignature();
1826            AccessFlags acc = field.getAccessFlagsObj();
1827 
1828            buf.beginListItem();
1829            buf.append(genFieldModifierString(acc));
1830            buf.append(' ');
1831            Formatter sigBuf = new Formatter(genHTML);
1832            new SignatureConverter(f_sig, sigBuf.getBuffer()).dispatchField();
1833            buf.append(sigBuf.toString().replace('/', '.'));
1834            buf.append(' ');
1835            buf.append(f_name);
1836            buf.append(';');
1837            // is it generic?
1838            if (f_genSig != null) {
1839               buf.append(" [signature ");
1840               buf.append(escapeHTMLSpecialChars(f_genSig.asString()));
1841               buf.append("] ");
1842            }
1843            buf.append(" (offset = " + field.getOffset() + ")");
1844            buf.endListItem();
1845          }
1846          buf.endList();
1847       }
1848       return buf.toString();
1849    }
1850 
1851    protected String genKlassHierarchyHref(InstanceKlass klass) {
1852       return genBaseHref() + "hierarchy=" + klass.getHandle();
1853    }
1854 
1855    protected String genKlassHierarchyTitle(InstanceKlass klass) {
1856       Formatter buf = new Formatter(genHTML);
1857       buf.append("Class Hierarchy of ");
1858       buf.append(genKlassTitle(klass));
1859       return buf.toString();
1860    }
1861 
1862    protected String genKlassHierarchyLink(InstanceKlass klass) {
1863       Formatter buf = new Formatter(genHTML);
1864       buf.link(genKlassHierarchyHref(klass), genKlassHierarchyTitle(klass));
1865       return buf.toString();
1866    }
1867 
1868    protected String genHTMLListForSubKlasses(InstanceKlass klass) {
1869       Formatter buf = new Formatter(genHTML);
1870       Klass subklass = klass.getSubklassKlass();
1871       if (subklass != null) {
1872          buf.beginList();
1873          while (subklass != null) {
1874             if (subklass instanceof InstanceKlass) {
1875                buf.li(genKlassLink((InstanceKlass)subklass));
1876             }
1877             subklass = subklass.getNextSiblingKlass();
1878          }
1879          buf.endList();
1880       }
1881       return buf.toString();
1882    }
1883 
1884    public String genHTMLForKlassHierarchy(InstanceKlass klass) {
1885       Formatter buf = new Formatter(genHTML);
1886       buf.genHTMLPrologue(genKlassHierarchyTitle(klass));
1887 
1888 
1889       buf.beginTag("pre");
1890       buf.append(genKlassLink(klass));
1891       buf.br();
1892       StringBuffer tabs = new StringBuffer(tab);
1893       InstanceKlass superKlass = klass;
1894       while ( (superKlass = (InstanceKlass) superKlass.getSuper()) != null ) {
1895          buf.append(tabs);
1896          buf.append(genKlassLink(superKlass));
1897          tabs.append(tab);
1898          buf.br();
1899       }
1900       buf.endTag("pre");
1901 
1902       // generate subklass list
1903       Klass subklass = klass.getSubklassKlass();
1904       if (subklass != null) {
1905          buf.h3("Direct Subclasses");
1906          buf.append(genHTMLListForSubKlasses(klass));
1907       }
1908 
1909       buf.genHTMLEpilogue();
1910       return buf.toString();
1911    }
1912 
1913    protected String genDumpKlassHref(InstanceKlass klass) {
1914       return genBaseHref() + "jcore=" + klass.getHandle();
1915    }
1916 
1917    protected String genDumpKlassLink(InstanceKlass klass) {
1918       if (!genHTML) return "";
1919 
1920       Formatter buf = new Formatter(genHTML);
1921       buf.link(genDumpKlassHref(klass), "Create .class File");
1922       return buf.toString();
1923    }
1924 
1925    public String genHTML(InstanceKlass klass) {
1926       Formatter buf = new Formatter(genHTML);
1927       buf.genHTMLPrologue(genKlassTitle(klass));
1928       InstanceKlass superKlass = (InstanceKlass) klass.getSuper();
1929 
1930       if (genHTML) {
1931           // super class tree and subclass list
1932           buf.beginTag("h3");
1933           buf.link(genKlassHierarchyHref(klass), "View Class Hierarchy");
1934           buf.endTag("h3");
1935       }
1936 
1937       // jcore - create .class link
1938       buf.h3(genDumpKlassLink(klass));
1939 
1940       // super class
1941       if (superKlass != null) {
1942          buf.h3("Super Class");
1943          buf.append(genKlassLink(superKlass));
1944       }
1945 
1946       // interfaces
1947       buf.append(genHTMLListForInterfaces(klass));
1948 
1949       // fields
1950       buf.append(genHTMLListForFields(klass));
1951 
1952       // methods
1953       buf.append(genHTMLListForMethods(klass));
1954 
1955       // constant pool link
1956       buf.h3("Constant Pool");
1957       buf.append(genConstantPoolLink(klass.getConstants()));
1958 
1959       buf.genHTMLEpilogue();
1960       return buf.toString();
1961    }
1962 
1963    protected sun.jvm.hotspot.debugger.Address parseAddress(String address) {
1964       VM vm = VM.getVM();
1965       sun.jvm.hotspot.debugger.Address addr = vm.getDebugger().parseAddress(address);
1966       return addr;
1967    }
1968 
1969    protected long addressToLong(sun.jvm.hotspot.debugger.Address addr) {
1970       return VM.getVM().getDebugger().getAddressValue(addr);
1971    }
1972 
1973    protected sun.jvm.hotspot.debugger.Address longToAddress(long addr) {
1974       return parseAddress("0x" + Long.toHexString(addr));
1975    }
1976 
1977    protected Oop getOopAtAddress(sun.jvm.hotspot.debugger.Address addr) {
1978       OopHandle oopHandle = addr.addOffsetToAsOopHandle(0);
1979       return VM.getVM().getObjectHeap().newOop(oopHandle);
1980    }
1981 
1982    protected Oop getOopAtAddress(String address) {
1983       sun.jvm.hotspot.debugger.Address addr = parseAddress(address);
1984       return getOopAtAddress(addr);
1985    }
1986 
1987    private void dumpKlass(InstanceKlass kls) throws IOException {
1988       String klassName = kls.getName().asString();
1989       klassName = klassName.replace('/', File.separatorChar);
1990       int index = klassName.lastIndexOf(File.separatorChar);
1991       File dir = null;
1992       if (index != -1) {
1993         String dirName = klassName.substring(0, index);
1994         dir =  new File(DUMP_KLASS_OUTPUT_DIR,  dirName);
1995       } else {
1996         dir = new File(DUMP_KLASS_OUTPUT_DIR);
1997       }
1998 
1999       dir.mkdirs();
2000       File f = new File(dir, klassName.substring(klassName.lastIndexOf(File.separatorChar) + 1)
2001                               + ".class");
2002       f.createNewFile();
2003       FileOutputStream fis = new FileOutputStream(f);
2004       ClassWriter cw = new ClassWriter(kls, fis);
2005       cw.write();
2006    }
2007 
2008    public String genDumpKlass(InstanceKlass kls) {
2009       try {
2010          dumpKlass(kls);
2011          Formatter buf = new Formatter(genHTML);
2012          buf.genHTMLPrologue(genKlassTitle(kls));
2013          buf.append(".class created for ");
2014          buf.append(genKlassLink(kls));
2015          buf.genHTMLEpilogue();
2016          return buf.toString();
2017       } catch(IOException exp) {
2018          return genHTMLErrorMessage(exp);
2019       }
2020    }
2021 
2022    protected String genJavaStackTraceTitle(JavaThread thread) {
2023       Formatter buf = new Formatter(genHTML);
2024       buf.append("Java Stack Trace for ");
2025       buf.append(thread.getThreadName());
2026       return buf.toString();
2027    }
2028 
2029    public String genHTMLForJavaStackTrace(JavaThread thread) {
2030       Formatter buf = new Formatter(genHTML);
2031       buf.genHTMLPrologue(genJavaStackTraceTitle(thread));
2032 
2033       buf.append("Thread state = ");
2034       buf.append(thread.getThreadState().toString());
2035       buf.br();
2036       buf.beginTag("pre");
2037       for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
2038          Method method = vf.getMethod();
2039          buf.append(" - ");
2040          buf.append(genMethodLink(method));
2041          buf.append(" @bci = " + vf.getBCI());
2042 
2043          int lineNumber = method.getLineNumberFromBCI(vf.getBCI());
2044          if (lineNumber != -1) {
2045             buf.append(", line = ");
2046             buf.append(lineNumber);
2047          }
2048 
2049          sun.jvm.hotspot.debugger.Address pc = vf.getFrame().getPC();
2050          if (pc != null) {
2051             buf.append(", pc = ");
2052             buf.link(genPCHref(addressToLong(pc)), pc.toString());
2053          }
2054 
2055          if (vf.isCompiledFrame()) {
2056             buf.append(" (Compiled");
2057          }
2058          else if (vf.isInterpretedFrame()) {
2059             buf.append(" (Interpreted");
2060          }
2061 
2062          if (vf.mayBeImpreciseDbg()) {
2063             buf.append("; information may be imprecise");
2064          }
2065          buf.append(")");
2066          buf.br();
2067       }
2068 
2069       buf.endTag("pre");
2070       buf.genHTMLEpilogue();
2071       return buf.toString();
2072    }
2073 
2074    public String genHTMLForHyperlink(String href) {
2075       if (href.startsWith("klass=")) {
2076          href = href.substring(href.indexOf('=') + 1);
2077          Oop obj = getOopAtAddress(href);
2078          if (Assert.ASSERTS_ENABLED) {
2079             Assert.that(obj instanceof InstanceKlass, "class= href with improper InstanceKlass!");
2080          }
2081          return genHTML((InstanceKlass) obj);
2082       } else if (href.startsWith("method=")) {
2083          href = href.substring(href.indexOf('=') + 1);
2084          Oop obj = getOopAtAddress(href);
2085          if (Assert.ASSERTS_ENABLED) {
2086             Assert.that(obj instanceof Method, "method= href with improper Method!");
2087          }
2088          return genHTML((Method) obj);
2089       } else if (href.startsWith("nmethod=")) {
2090          String addr = href.substring(href.indexOf('=') + 1);
2091          Object obj = VMObjectFactory.newObject(NMethod.class, parseAddress(addr));
2092          if (Assert.ASSERTS_ENABLED) {
2093             Assert.that(obj instanceof NMethod, "nmethod= href with improper NMethod!");
2094          }
2095          return genHTML((NMethod) obj);
2096       } else if (href.startsWith("pc=")) {
2097          String address = href.substring(href.indexOf('=') + 1);
2098          return genHTML(parseAddress(address));
2099       } else if (href.startsWith("pc_multiple=")) {
2100          int indexOfComma = href.indexOf(',');
2101          if (indexOfComma == -1) {
2102             String firstPC = href.substring(href.indexOf('=') + 1);
2103             return genHTMLForRawDisassembly(parseAddress(firstPC), null);
2104          } else {
2105             String firstPC = href.substring(href.indexOf('=') + 1, indexOfComma);
2106             return genHTMLForRawDisassembly(parseAddress(firstPC), href.substring(indexOfComma + 1));
2107          }
2108       } else if (href.startsWith("interp_codelets")) {
2109          return genInterpreterCodeletLinksPage();
2110       } else if (href.startsWith("hierarchy=")) {
2111          href = href.substring(href.indexOf('=') + 1);
2112          Oop obj = getOopAtAddress(href);
2113          if (Assert.ASSERTS_ENABLED) {
2114             Assert.that(obj instanceof InstanceKlass, "class= href with improper InstanceKlass!");
2115          }
2116          return genHTMLForKlassHierarchy((InstanceKlass) obj);
2117       } else if (href.startsWith("cpool=")) {
2118          href = href.substring(href.indexOf('=') + 1);
2119          Oop obj = getOopAtAddress(href);
2120          if (Assert.ASSERTS_ENABLED) {
2121             Assert.that(obj instanceof ConstantPool, "cpool= href with improper ConstantPool!");
2122          }
2123          return genHTML((ConstantPool) obj);
2124       } else if (href.startsWith("jcore=")) {
2125          href = href.substring(href.indexOf('=') + 1);
2126          Oop obj = getOopAtAddress(href);
2127          if (Assert.ASSERTS_ENABLED) {
2128             Assert.that(obj instanceof InstanceKlass, "jcore= href with improper InstanceKlass!");
2129          }
2130          return genDumpKlass((InstanceKlass) obj);
2131       } else if (href.startsWith("jcore_multiple=")) {
2132          href = href.substring(href.indexOf('=') + 1);
2133          Formatter buf = new Formatter(genHTML);
2134          buf.genHTMLPrologue();
2135          StringTokenizer st = new StringTokenizer(href, ",");
2136          while (st.hasMoreTokens()) {
2137             Oop obj = getOopAtAddress(st.nextToken());
2138             if (Assert.ASSERTS_ENABLED) {
2139                Assert.that(obj instanceof InstanceKlass, "jcore_multiple= href with improper InstanceKlass!");
2140             }
2141 
2142             InstanceKlass kls = (InstanceKlass) obj;
2143             try {
2144                dumpKlass(kls);
2145                buf.append(".class created for ");
2146                buf.append(genKlassLink(kls));
2147             } catch(Exception exp) {
2148                buf.bold("can't .class for " +
2149                         genKlassTitle(kls) +
2150                         " : " +
2151                         exp.getMessage());
2152             }
2153             buf.br();
2154          }
2155 
2156          buf.genHTMLEpilogue();
2157          return buf.toString();
2158       } else {
2159          if (Assert.ASSERTS_ENABLED) {
2160             Assert.that(false, "unknown href link!");
2161          }
2162          return null;
2163       }
2164    }
2165 }