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