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.INT_IN_LONG) {
1139             buf.append("int");
1140          } else if (type == Location.Type.LNG) {
1141             buf.append("long");
1142          } else if (type == Location.Type.FLOAT_IN_DBL) {
1143             buf.append("float");
1144          } else if (type == Location.Type.DBL) {
1145             buf.append("double");
1146          } else if (type == Location.Type.ADDR) {
1147             buf.append("address");
1148          } else if (type == Location.Type.INVALID) {
1149             buf.append("invalid");
1150          }
1151       }
1152       return buf.toString();
1153    }
1154 
1155    private String scopeValueAsString(ScopeValue sv) {
1156       Formatter buf = new Formatter(genHTML);
1157       if (sv.isConstantInt()) {
1158          buf.append("int ");
1159          ConstantIntValue intValue = (ConstantIntValue) sv;
1160          buf.append(Integer.toString(intValue.getValue()));
1161       } else if (sv.isConstantLong()) {
1162          buf.append("long ");
1163          ConstantLongValue longValue = (ConstantLongValue) sv;
1164          buf.append(Long.toString(longValue.getValue()));
1165          buf.append("L");
1166       } else if (sv.isConstantDouble()) {
1167          buf.append("double ");
1168          ConstantDoubleValue dblValue = (ConstantDoubleValue) sv;
1169          buf.append(Double.toString(dblValue.getValue()));
1170          buf.append("D");
1171       } else if (sv.isConstantOop()) {
1172          buf.append("oop ");
1173          ConstantOopReadValue oopValue = (ConstantOopReadValue) sv;
1174          OopHandle oopHandle = oopValue.getValue();
1175          if (oopHandle != null) {
1176             buf.append(oopHandle.toString());
1177          } else {
1178             buf.append("null");
1179          }
1180       } else if (sv.isLocation()) {
1181          LocationValue lvalue = (LocationValue) sv;
1182          Location loc = lvalue.getLocation();
1183          if (loc != null) {
1184             buf.append(locationAsString(loc));
1185          } else {
1186             buf.append("null");
1187          }
1188       }
1189       return buf.toString();
1190    }
1191 
1192    protected String genHTMLForScopeValues(ScopeDesc sd, boolean locals, List values) {
1193       int length = values.size();
1194       Formatter buf = new Formatter(genHTML);
1195       buf.append(locals? "locals " : "expressions ");
1196       for (int i = 0; i < length; i++) {
1197          ScopeValue sv = (ScopeValue) values.get(i);
1198          if (sv == null) {
1199             continue;
1200          }
1201          buf.append('(');
1202          if (locals) {
1203             Symbol name = sd.getMethod().getLocalVariableName(sd.getBCI(), i);
1204             if (name != null) {
1205                buf.append("'");
1206                buf.append(name.asString());
1207                buf.append('\'');
1208             } else {
1209                buf.append("[");
1210                buf.append(Integer.toString(i));
1211                buf.append(']');
1212             }
1213          } else {
1214             buf.append("[");
1215             buf.append(Integer.toString(i));
1216             buf.append(']');
1217          }
1218 
1219          buf.append(", ");
1220          buf.append(scopeValueAsString(sv));
1221          buf.append(") ");
1222       }
1223 
1224       return buf.toString();
1225    }
1226 
1227    protected String genHTMLForLocals(ScopeDesc sd, List locals) {
1228       return genHTMLForScopeValues(sd, true, locals);
1229    }
1230 
1231    protected String genHTMLForExpressions(ScopeDesc sd, List expressions) {
1232       return genHTMLForScopeValues(sd, false, expressions);
1233    }
1234 
1235    protected String genHTMLForMonitors(ScopeDesc sd, List monitors) {
1236       int length = monitors.size();
1237       Formatter buf = new Formatter(genHTML);
1238       buf.append("monitors ");
1239       for (int i = 0; i < length; i++) {
1240          MonitorValue mv = (MonitorValue) monitors.get(i);
1241          if (mv == null) {
1242             continue;
1243          }
1244          buf.append("(owner = ");
1245          ScopeValue owner = mv.owner();
1246          if (owner != null) {
1247             buf.append(scopeValueAsString(owner));
1248          } else {
1249             buf.append("null");
1250          }
1251          buf.append(", lock = ");
1252 
1253          Location loc = mv.basicLock();
1254          if (loc != null) {
1255             buf.append(locationAsString(loc));
1256          } else {
1257             buf.append("null");
1258          }
1259          buf.append(") ");
1260       }
1261       return buf.toString();
1262    }
1263 
1264    public String genHTML(final NMethod nmethod) {
1265       try {
1266          final Formatter buf = new Formatter(genHTML);
1267          buf.genHTMLPrologue(genNMethodTitle(nmethod));
1268          buf.h3("Method");
1269          buf.append(genMethodAndKlassLink(nmethod.getMethod()));
1270 
1271          buf.h3("Compiled Code");
1272          sun.jvm.hotspot.debugger.Address codeBegin = nmethod.codeBegin();
1273          sun.jvm.hotspot.debugger.Address codeEnd   = nmethod.codeEnd();
1274          final int codeSize = (int)codeEnd.minus(codeBegin);
1275          final long startPc = addressToLong(codeBegin);
1276          final byte[] code = new byte[codeSize];
1277          for (int i=0; i < code.length; i++)
1278             code[i] = codeBegin.getJByteAt(i);
1279 
1280          final long verifiedEntryPoint = addressToLong(nmethod.getVerifiedEntryPoint());
1281          final long entryPoint = addressToLong(nmethod.getEntryPoint());
1282          final Map safepoints = nmethod.getSafepoints();
1283 
1284          final SymbolFinder symFinder = createSymbolFinder();
1285          final Disassembler disasm = createDisassembler(startPc, code);
1286          class NMethodVisitor implements InstructionVisitor {
1287             boolean prevWasCall;
1288             public void prologue() {
1289                prevWasCall = false;
1290             }
1291 
1292             public void visit(long currentPc, Instruction instr) {
1293                String href = null;
1294                if (instr.isCall()) {
1295                   CallInstruction call = (CallInstruction) instr;
1296                   sun.jvm.hotspot.asm.Address addr = call.getBranchDestination();
1297                   href = genPCHref(currentPc, addr);
1298                }
1299 
1300                if (currentPc == verifiedEntryPoint) {
1301                    buf.bold("Verified Entry Point"); buf.br();
1302                }
1303                if (currentPc == entryPoint) {
1304                    buf.bold(">Entry Point"); buf.br();
1305                }
1306 
1307                PCDesc pcDesc = (PCDesc) safepoints.get(longToAddress(currentPc));
1308 
1309                boolean isSafepoint = (pcDesc != null);
1310                if (isSafepoint && prevWasCall) {
1311                   buf.append(genSafepointInfo(nmethod, pcDesc));
1312                }
1313 
1314                buf.append("0x");
1315                buf.append(Long.toHexString(currentPc));
1316                buf.append(':');
1317                buf.append(tab);
1318 
1319                if (href != null) {
1320                   buf.link(href, instr.asString(currentPc, symFinder));
1321                } else {
1322                   buf.append(instr.asString(currentPc, symFinder));
1323                }
1324 
1325                if (isSafepoint && !prevWasCall) {
1326                   buf.append(genSafepointInfo(nmethod, pcDesc));
1327                }
1328 
1329                buf.br();
1330                prevWasCall = instr.isCall();
1331             }
1332 
1333             public void epilogue() {
1334             }
1335          };
1336 
1337          disasm.decode(new NMethodVisitor());
1338 
1339          sun.jvm.hotspot.debugger.Address stubBegin = nmethod.stubBegin();
1340          if (stubBegin != null) {
1341             sun.jvm.hotspot.debugger.Address stubEnd   = nmethod.stubEnd();
1342             buf.h3("Stub");
1343             long stubStartPc = addressToLong(stubBegin);
1344             long stubEndPc = addressToLong(stubEnd);
1345             int range = (int) (stubEndPc - stubStartPc);
1346             byte[] stubCode = readBuffer(stubBegin, range);
1347             Disassembler disasm2 = createDisassembler(stubStartPc, stubCode);
1348             disasm2.decode(new NMethodVisitor());
1349          }
1350          buf.genHTMLEpilogue();
1351          return buf.toString();
1352       } catch (Exception exp) {
1353          return genHTMLErrorMessage(exp);
1354       }
1355    }
1356 
1357   public String genHTML(final CodeBlob blob) {
1358       try {
1359          final Formatter buf = new Formatter(genHTML);
1360          buf.genHTMLPrologue(genCodeBlobTitle(blob));
1361          buf.h3("CodeBlob");
1362 
1363          buf.h3("Compiled Code");
1364          final sun.jvm.hotspot.debugger.Address codeBegin = blob.instructionsBegin();
1365          final int codeSize = blob.getInstructionsSize();
1366          final long startPc = addressToLong(codeBegin);
1367          final byte[] code = new byte[codeSize];
1368          for (int i=0; i < code.length; i++)
1369             code[i] = codeBegin.getJByteAt(i);
1370 
1371          final SymbolFinder symFinder = createSymbolFinder();
1372          final Disassembler disasm = createDisassembler(startPc, code);
1373          class CodeBlobVisitor implements InstructionVisitor {
1374             OopMapSet maps;
1375             OopMap curMap;
1376             int curMapIndex;
1377             long curMapOffset;
1378             public void prologue() {
1379               maps = blob.getOopMaps();
1380               if (maps != null && (maps.getSize() > 0)) {
1381                 curMap = maps.getMapAt(0);
1382                 if (curMap != null) {
1383                   curMapOffset = curMap.getOffset();
1384                 }
1385               }
1386             }
1387 
1388             public void visit(long currentPc, Instruction instr) {
1389                String href = null;
1390                if (instr.isCall()) {
1391                   CallInstruction call = (CallInstruction) instr;
1392                   sun.jvm.hotspot.asm.Address addr = call.getBranchDestination();
1393                   href = genPCHref(currentPc, addr);
1394                }
1395 
1396                buf.append("0x");
1397                buf.append(Long.toHexString(currentPc));
1398                buf.append(':');
1399                buf.append(tab);
1400 
1401                if (href != null) {
1402                   buf.link(href, instr.asString(currentPc, symFinder));
1403                } else {
1404                    buf.append(instr.asString(currentPc, symFinder));
1405                }
1406                buf.br();
1407 
1408                // See whether we have an oop map at this PC
1409                if (curMap != null) {
1410                  long curOffset = currentPc - startPc;
1411                  if (curOffset == curMapOffset) {
1412                    buf.append(genOopMapInfo(curMap));
1413                    if (++curMapIndex >= maps.getSize()) {
1414                      curMap = null;
1415                    } else {
1416                      curMap = maps.getMapAt(curMapIndex);
1417                      if (curMap != null) {
1418                        curMapOffset = curMap.getOffset();
1419                      }
1420                    }
1421                  }
1422                }
1423             }
1424 
1425             public void epilogue() {
1426             }
1427          };
1428 
1429          disasm.decode(new CodeBlobVisitor());
1430 
1431          buf.genHTMLEpilogue();
1432          return buf.toString();
1433       } catch (Exception exp) {
1434          return genHTMLErrorMessage(exp);
1435       }
1436    }
1437 
1438    protected String genInterpreterCodeletTitle(InterpreterCodelet codelet) {
1439       Formatter buf = new Formatter(genHTML);
1440       buf.append("Interpreter codelet [");
1441       buf.append(codelet.codeBegin().toString());
1442       buf.append(',');
1443       buf.append(codelet.codeEnd().toString());
1444       buf.append(") - ");
1445       buf.append(codelet.getDescription());
1446       return buf.toString();
1447    }
1448 
1449    protected String genInterpreterCodeletLinkPageHref(StubQueue stubq) {
1450       return genBaseHref() + "interp_codelets";
1451    }
1452 
1453    public String genInterpreterCodeletLinksPage() {
1454       Formatter buf = new Formatter(genHTML);
1455       buf.genHTMLPrologue("Interpreter Codelets");
1456       buf.beginTag("ul");
1457 
1458       Interpreter interp = VM.getVM().getInterpreter();
1459       StubQueue code = interp.getCode();
1460       InterpreterCodelet stub = (InterpreterCodelet) code.getFirst();
1461       while (stub != null) {
1462          buf.beginTag("li");
1463          sun.jvm.hotspot.debugger.Address addr = stub.codeBegin();
1464          buf.link(genPCHref(addressToLong(addr)), stub.getDescription() + " @" + addr);
1465          buf.endTag("li");
1466          stub = (InterpreterCodelet) code.getNext(stub);
1467       }
1468 
1469       buf.endTag("ul");
1470       buf.genHTMLEpilogue();
1471       return buf.toString();
1472    }
1473 
1474    public String genHTML(InterpreterCodelet codelet) {
1475       Formatter buf = new Formatter(genHTML);
1476       buf.genHTMLPrologue(genInterpreterCodeletTitle(codelet));
1477       Interpreter interp = VM.getVM().getInterpreter();
1478       StubQueue stubq = interp.getCode();
1479 
1480       if (genHTML) {
1481          buf.beginTag("h3");
1482          buf.link(genInterpreterCodeletLinkPageHref(stubq), "View links for all codelets");
1483          buf.endTag("h3");
1484          buf.br();
1485       }
1486 
1487       Stub prev = stubq.getPrev(codelet);
1488       if (prev != null) {
1489          if (genHTML) {
1490             buf.beginTag("h3");
1491             buf.link(genPCHref(addressToLong(prev.codeBegin())), "View Previous Codelet");
1492             buf.endTag("h3");
1493             buf.br();
1494          } else {
1495             buf.h3("Previous Codelet = 0x" + Long.toHexString(addressToLong(prev.codeBegin())));
1496          }
1497       }
1498 
1499       buf.h3("Code");
1500       long stubStartPc = addressToLong(codelet.codeBegin());
1501       long stubEndPc = addressToLong(codelet.codeEnd());
1502       int range = (int) (stubEndPc - stubStartPc);
1503       byte[] stubCode = readBuffer(codelet.codeBegin(), range);
1504       Disassembler disasm = createDisassembler(stubStartPc, stubCode);
1505       disasm.decode(new RawCodeVisitor(buf));
1506 
1507 
1508       Stub next = stubq.getNext(codelet);
1509       if (next != null) {
1510          if (genHTML) {
1511             buf.beginTag("h3");
1512             buf.link(genPCHref(addressToLong(next.codeBegin())), "View Next Codelet");
1513             buf.endTag("h3");
1514          } else {
1515             buf.h3("Next Codelet = 0x" + Long.toHexString(addressToLong(next.codeBegin())));
1516          }
1517       }
1518 
1519       buf.genHTMLEpilogue();
1520       return buf.toString();
1521    }
1522 
1523    protected String genDumpKlassesTitle(InstanceKlass[] klasses) {
1524       return (klasses.length == 1) ? "Create .class for this class"
1525                                    : "Create .class for all classes";
1526    }
1527 
1528    protected String genDumpKlassesHref(InstanceKlass[] klasses) {
1529       StringBuffer buf = new StringBuffer(genBaseHref());
1530       buf.append("jcore_multiple=");
1531       for (int k = 0; k < klasses.length; k++) {
1532          buf.append(klasses[k].getHandle().toString());
1533          buf.append(',');
1534       }
1535       return buf.toString();
1536    }
1537 
1538    protected String genDumpKlassesLink(InstanceKlass[] klasses) {
1539       if (!genHTML) return "";
1540 
1541       Formatter buf = new Formatter(genHTML);
1542       buf.link(genDumpKlassesHref(klasses), genDumpKlassesTitle(klasses));
1543       return buf.toString();
1544    }
1545 
1546    public String genHTMLForKlassNames(InstanceKlass[] klasses) {
1547       try {
1548          Formatter buf = new Formatter(genHTML);
1549          buf.genHTMLPrologue();
1550          buf.h3(genDumpKlassesLink(klasses));
1551 
1552          buf.append(genHTMLListForKlassNames(klasses));
1553          buf.genHTMLEpilogue();
1554          return buf.toString();
1555       } catch (Exception exp) {
1556          return genHTMLErrorMessage(exp);
1557       }
1558    }
1559 
1560    protected String genHTMLListForKlassNames(InstanceKlass[] klasses) {
1561       final Formatter buf = new Formatter(genHTML);
1562       buf.beginTable(0);
1563       for (int i = 0; i < klasses.length; i++) {
1564          InstanceKlass ik = klasses[i];
1565          buf.beginTag("tr");
1566          buf.cell(genKlassLink(ik));
1567          buf.endTag("tr");
1568       }
1569 
1570       buf.endTable();
1571       return buf.toString();
1572    }
1573 
1574    public String genHTMLForMethodNames(InstanceKlass klass) {
1575       try {
1576          Formatter buf = new Formatter(genHTML);
1577          buf.genHTMLPrologue();
1578          buf.append(genHTMLListForMethods(klass));
1579          buf.genHTMLEpilogue();
1580          return buf.toString();
1581       } catch (Exception exp) {
1582          return genHTMLErrorMessage(exp);
1583       }
1584    }
1585 
1586    protected String genHTMLListForMethods(InstanceKlass klass) {
1587       Formatter buf = new Formatter(genHTML);
1588       ObjArray methods = klass.getMethods();
1589       int numMethods = (int) methods.getLength();
1590       if (numMethods != 0) {
1591          buf.h3("Methods");
1592          buf.beginTag("ul");
1593          for (int m = 0; m < numMethods; m++) {
1594             Method mtd = (Method) methods.getObjAt(m);
1595             buf.li(genMethodLink(mtd) + ";");
1596          }
1597          buf.endTag("ul");
1598       }
1599       return buf.toString();
1600    }
1601 
1602    protected String genHTMLListForInterfaces(InstanceKlass klass) {
1603       try {
1604          Formatter buf = new Formatter(genHTML);
1605          ObjArray interfaces = klass.getLocalInterfaces();
1606          int numInterfaces = (int) interfaces.getLength();
1607          if (numInterfaces != 0) {
1608             buf.h3("Interfaces");
1609             buf.beginTag("ul");
1610             for (int i = 0; i < numInterfaces; i++) {
1611                InstanceKlass inf = (InstanceKlass) interfaces.getObjAt(i);
1612                buf.li(genKlassLink(inf));
1613             }
1614             buf.endTag("ul");
1615          }
1616          return buf.toString();
1617       } catch (Exception exp) {
1618          return genHTMLErrorMessage(exp);
1619       }
1620    }
1621 
1622    protected String genFieldModifierString(AccessFlags acc) {
1623       Formatter buf = new Formatter(genHTML);
1624       if (acc.isPrivate()) {
1625          buf.append("private ");
1626       } else if (acc.isProtected()) {
1627          buf.append("protected ");
1628       } else if (acc.isPublic()) {
1629          buf.append("public ");
1630       }
1631 
1632       if (acc.isStatic()) {
1633          buf.append("static ");
1634       }
1635 
1636       if (acc.isFinal()) {
1637          buf.append("final ");
1638       }
1639       if (acc.isVolatile()) {
1640          buf.append("volatile ");
1641       }
1642       if (acc.isTransient()) {
1643          buf.append("transient ");
1644       }
1645 
1646       // javac generated flags
1647       if (acc.isSynthetic()) {
1648          buf.append("[synthetic] ");
1649       }
1650       return buf.toString();
1651    }
1652 
1653    public String genHTMLForFieldNames(InstanceKlass klass) {
1654       try {
1655          Formatter buf = new Formatter(genHTML);
1656          buf.genHTMLPrologue();
1657          buf.append(genHTMLListForFields(klass));
1658          buf.genHTMLEpilogue();
1659          return buf.toString();
1660       } catch (Exception exp) {
1661          return genHTMLErrorMessage(exp);
1662       }
1663    }
1664 
1665    protected String genHTMLListForFields(InstanceKlass klass) {
1666       Formatter buf = new Formatter(genHTML);
1667       TypeArray fields = klass.getFields();
1668       int numFields = (int) fields.getLength();
1669       ConstantPool cp = klass.getConstants();
1670       if (numFields != 0) {
1671          buf.h3("Fields");
1672          buf.beginList();
1673          for (int f = 0; f < numFields; f += InstanceKlass.NEXT_OFFSET) {
1674            int nameIndex = fields.getShortAt(f + InstanceKlass.NAME_INDEX_OFFSET);
1675            int sigIndex  = fields.getShortAt(f + InstanceKlass.SIGNATURE_INDEX_OFFSET);
1676            int genSigIndex = fields.getShortAt(f + InstanceKlass.GENERIC_SIGNATURE_INDEX_OFFSET);
1677            Symbol f_name = cp.getSymbolAt(nameIndex);
1678            Symbol f_sig  = cp.getSymbolAt(sigIndex);
1679            Symbol f_genSig = (genSigIndex != 0)? cp.getSymbolAt(genSigIndex) : null;
1680            AccessFlags acc = new AccessFlags(fields.getShortAt(f + InstanceKlass.ACCESS_FLAGS_OFFSET));
1681 
1682            buf.beginTag("li");
1683            buf.append(genFieldModifierString(acc));
1684            buf.append(' ');
1685            Formatter sigBuf = new Formatter(genHTML);
1686            new SignatureConverter(f_sig, sigBuf.getBuffer()).dispatchField();
1687            buf.append(sigBuf.toString().replace('/', '.'));
1688            buf.append(' ');
1689            buf.append(f_name.asString());
1690            buf.append(';');
1691            // is it generic?
1692            if (f_genSig != null) {
1693               buf.append(" [signature ");
1694               buf.append(escapeHTMLSpecialChars(f_genSig.asString()));
1695               buf.append("] ");
1696            }
1697            buf.endTag("li");
1698          }
1699          buf.endList();
1700       }
1701       return buf.toString();
1702    }
1703 
1704    protected String genKlassHierarchyHref(InstanceKlass klass) {
1705       return genBaseHref() + "hierarchy=" + klass.getHandle();
1706    }
1707 
1708    protected String genKlassHierarchyTitle(InstanceKlass klass) {
1709       Formatter buf = new Formatter(genHTML);
1710       buf.append("Class Hierarchy of ");
1711       buf.append(genKlassTitle(klass));
1712       return buf.toString();
1713    }
1714 
1715    protected String genKlassHierarchyLink(InstanceKlass klass) {
1716       Formatter buf = new Formatter(genHTML);
1717       buf.link(genKlassHierarchyHref(klass), genKlassHierarchyTitle(klass));
1718       return buf.toString();
1719    }
1720 
1721    protected String genHTMLListForSubKlasses(InstanceKlass klass) {
1722       Formatter buf = new Formatter(genHTML);
1723       Klass subklass = klass.getSubklassKlass();
1724       if (subklass != null) {
1725          buf.beginList();
1726          while (subklass != null) {
1727             if (subklass instanceof InstanceKlass) {
1728                buf.li(genKlassLink((InstanceKlass)subklass));
1729             }
1730             subklass = subklass.getNextSiblingKlass();
1731          }
1732          buf.endList();
1733       }
1734       return buf.toString();
1735    }
1736 
1737    public String genHTMLForKlassHierarchy(InstanceKlass klass) {
1738       Formatter buf = new Formatter(genHTML);
1739       buf.genHTMLPrologue(genKlassHierarchyTitle(klass));
1740 
1741 
1742       buf.beginTag("pre");
1743       buf.append(genKlassLink(klass));
1744       buf.br();
1745       StringBuffer tabs = new StringBuffer(tab);
1746       InstanceKlass superKlass = klass;
1747       while ( (superKlass = (InstanceKlass) superKlass.getSuper()) != null ) {
1748          buf.append(tabs);
1749          buf.append(genKlassLink(superKlass));
1750          tabs.append(tab);
1751          buf.br();
1752       }
1753       buf.endTag("pre");
1754 
1755       // generate subklass list
1756       Klass subklass = klass.getSubklassKlass();
1757       if (subklass != null) {
1758          buf.h3("Direct Subclasses");
1759          buf.append(genHTMLListForSubKlasses(klass));
1760       }
1761 
1762       buf.genHTMLEpilogue();
1763       return buf.toString();
1764    }
1765 
1766    protected String genDumpKlassHref(InstanceKlass klass) {
1767       return genBaseHref() + "jcore=" + klass.getHandle();
1768    }
1769 
1770    protected String genDumpKlassLink(InstanceKlass klass) {
1771       if (!genHTML) return "";
1772 
1773       Formatter buf = new Formatter(genHTML);
1774       buf.link(genDumpKlassHref(klass), "Create .class File");
1775       return buf.toString();
1776    }
1777 
1778    public String genHTML(InstanceKlass klass) {
1779       Formatter buf = new Formatter(genHTML);
1780       buf.genHTMLPrologue(genKlassTitle(klass));
1781       InstanceKlass superKlass = (InstanceKlass) klass.getSuper();
1782 
1783       if (genHTML) {
1784           // super class tree and subclass list
1785           buf.beginTag("h3");
1786           buf.link(genKlassHierarchyHref(klass), "View Class Hierarchy");
1787           buf.endTag("h3");
1788       }
1789 
1790       // jcore - create .class link
1791       buf.h3(genDumpKlassLink(klass));
1792 
1793       // super class
1794       if (superKlass != null) {
1795          buf.h3("Super Class");
1796          buf.append(genKlassLink(superKlass));
1797       }
1798 
1799       // interfaces
1800       buf.append(genHTMLListForInterfaces(klass));
1801 
1802       // fields
1803       buf.append(genHTMLListForFields(klass));
1804 
1805       // methods
1806       buf.append(genHTMLListForMethods(klass));
1807 
1808       // constant pool link
1809       buf.h3("Constant Pool");
1810       buf.append(genConstantPoolLink(klass.getConstants()));
1811 
1812       buf.genHTMLEpilogue();
1813       return buf.toString();
1814    }
1815 
1816    protected sun.jvm.hotspot.debugger.Address parseAddress(String address) {
1817       VM vm = VM.getVM();
1818       sun.jvm.hotspot.debugger.Address addr = vm.getDebugger().parseAddress(address);
1819       return addr;
1820    }
1821 
1822    protected long addressToLong(sun.jvm.hotspot.debugger.Address addr) {
1823       return VM.getVM().getDebugger().getAddressValue(addr);
1824    }
1825 
1826    protected sun.jvm.hotspot.debugger.Address longToAddress(long addr) {
1827       return parseAddress("0x" + Long.toHexString(addr));
1828    }
1829 
1830    protected Oop getOopAtAddress(sun.jvm.hotspot.debugger.Address addr) {
1831       OopHandle oopHandle = addr.addOffsetToAsOopHandle(0);
1832       return VM.getVM().getObjectHeap().newOop(oopHandle);
1833    }
1834 
1835    protected Oop getOopAtAddress(String address) {
1836       sun.jvm.hotspot.debugger.Address addr = parseAddress(address);
1837       return getOopAtAddress(addr);
1838    }
1839 
1840    private void dumpKlass(InstanceKlass kls) throws IOException {
1841       String klassName = kls.getName().asString();
1842       klassName = klassName.replace('/', File.separatorChar);
1843       int index = klassName.lastIndexOf(File.separatorChar);
1844       File dir = null;
1845       if (index != -1) {
1846         String dirName = klassName.substring(0, index);
1847         dir =  new File(DUMP_KLASS_OUTPUT_DIR,  dirName);
1848       } else {
1849         dir = new File(DUMP_KLASS_OUTPUT_DIR);
1850       }
1851 
1852       dir.mkdirs();
1853       File f = new File(dir, klassName.substring(klassName.lastIndexOf(File.separatorChar) + 1)
1854                               + ".class");
1855       f.createNewFile();
1856       FileOutputStream fis = new FileOutputStream(f);
1857       ClassWriter cw = new ClassWriter(kls, fis);
1858       cw.write();
1859    }
1860 
1861    public String genDumpKlass(InstanceKlass kls) {
1862       try {
1863          dumpKlass(kls);
1864          Formatter buf = new Formatter(genHTML);
1865          buf.genHTMLPrologue(genKlassTitle(kls));
1866          buf.append(".class created for ");
1867          buf.append(genKlassLink(kls));
1868          buf.genHTMLEpilogue();
1869          return buf.toString();
1870       } catch(IOException exp) {
1871          return genHTMLErrorMessage(exp);
1872       }
1873    }
1874 
1875    protected String genJavaStackTraceTitle(JavaThread thread) {
1876       Formatter buf = new Formatter(genHTML);
1877       buf.append("Java Stack Trace for ");
1878       buf.append(thread.getThreadName());
1879       return buf.toString();
1880    }
1881 
1882    public String genHTMLForJavaStackTrace(JavaThread thread) {
1883       Formatter buf = new Formatter(genHTML);
1884       buf.genHTMLPrologue(genJavaStackTraceTitle(thread));
1885 
1886       buf.append("Thread state = ");
1887       buf.append(thread.getThreadState().toString());
1888       buf.br();
1889       buf.beginTag("pre");
1890       for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
1891          Method method = vf.getMethod();
1892          buf.append(" - ");
1893          buf.append(genMethodLink(method));
1894          buf.append(" @bci = " + vf.getBCI());
1895 
1896          int lineNumber = method.getLineNumberFromBCI(vf.getBCI());
1897          if (lineNumber != -1) {
1898             buf.append(", line = ");
1899             buf.append(lineNumber);
1900          }
1901 
1902          sun.jvm.hotspot.debugger.Address pc = vf.getFrame().getPC();
1903          if (pc != null) {
1904             buf.append(", pc = ");
1905             buf.link(genPCHref(addressToLong(pc)), pc.toString());
1906          }
1907 
1908          if (vf.isCompiledFrame()) {
1909             buf.append(" (Compiled");
1910          }
1911          else if (vf.isInterpretedFrame()) {
1912             buf.append(" (Interpreted");
1913          }
1914 
1915          if (vf.mayBeImpreciseDbg()) {
1916             buf.append("; information may be imprecise");
1917          }
1918          buf.append(")");
1919          buf.br();
1920       }
1921 
1922       buf.endTag("pre");
1923       buf.genHTMLEpilogue();
1924       return buf.toString();
1925    }
1926 
1927    public String genHTMLForHyperlink(String href) {
1928       if (href.startsWith("klass=")) {
1929          href = href.substring(href.indexOf('=') + 1);
1930          Oop obj = getOopAtAddress(href);
1931          if (Assert.ASSERTS_ENABLED) {
1932             Assert.that(obj instanceof InstanceKlass, "class= href with improper InstanceKlass!");
1933          }
1934          return genHTML((InstanceKlass) obj);
1935       } else if (href.startsWith("method=")) {
1936          href = href.substring(href.indexOf('=') + 1);
1937          Oop obj = getOopAtAddress(href);
1938          if (Assert.ASSERTS_ENABLED) {
1939             Assert.that(obj instanceof Method, "method= href with improper Method!");
1940          }
1941          return genHTML((Method) obj);
1942       } else if (href.startsWith("nmethod=")) {
1943          String addr = href.substring(href.indexOf('=') + 1);
1944          Object obj = VMObjectFactory.newObject(NMethod.class, parseAddress(addr));
1945          if (Assert.ASSERTS_ENABLED) {
1946             Assert.that(obj instanceof NMethod, "nmethod= href with improper NMethod!");
1947          }
1948          return genHTML((NMethod) obj);
1949       } else if (href.startsWith("pc=")) {
1950          String address = href.substring(href.indexOf('=') + 1);
1951          return genHTML(parseAddress(address));
1952       } else if (href.startsWith("pc_multiple=")) {
1953          int indexOfComma = href.indexOf(',');
1954          if (indexOfComma == -1) {
1955             String firstPC = href.substring(href.indexOf('=') + 1);
1956             return genHTMLForRawDisassembly(parseAddress(firstPC), null);
1957          } else {
1958             String firstPC = href.substring(href.indexOf('=') + 1, indexOfComma);
1959             return genHTMLForRawDisassembly(parseAddress(firstPC), href.substring(indexOfComma + 1));
1960          }
1961       } else if (href.startsWith("interp_codelets")) {
1962          return genInterpreterCodeletLinksPage();
1963       } else if (href.startsWith("hierarchy=")) {
1964          href = href.substring(href.indexOf('=') + 1);
1965          Oop obj = getOopAtAddress(href);
1966          if (Assert.ASSERTS_ENABLED) {
1967             Assert.that(obj instanceof InstanceKlass, "class= href with improper InstanceKlass!");
1968          }
1969          return genHTMLForKlassHierarchy((InstanceKlass) obj);
1970       } else if (href.startsWith("cpool=")) {
1971          href = href.substring(href.indexOf('=') + 1);
1972          Oop obj = getOopAtAddress(href);
1973          if (Assert.ASSERTS_ENABLED) {
1974             Assert.that(obj instanceof ConstantPool, "cpool= href with improper ConstantPool!");
1975          }
1976          return genHTML((ConstantPool) obj);
1977       } else if (href.startsWith("jcore=")) {
1978          href = href.substring(href.indexOf('=') + 1);
1979          Oop obj = getOopAtAddress(href);
1980          if (Assert.ASSERTS_ENABLED) {
1981             Assert.that(obj instanceof InstanceKlass, "jcore= href with improper InstanceKlass!");
1982          }
1983          return genDumpKlass((InstanceKlass) obj);
1984       } else if (href.startsWith("jcore_multiple=")) {
1985          href = href.substring(href.indexOf('=') + 1);
1986          Formatter buf = new Formatter(genHTML);
1987          buf.genHTMLPrologue();
1988          StringTokenizer st = new StringTokenizer(href, ",");
1989          while (st.hasMoreTokens()) {
1990             Oop obj = getOopAtAddress(st.nextToken());
1991             if (Assert.ASSERTS_ENABLED) {
1992                Assert.that(obj instanceof InstanceKlass, "jcore_multiple= href with improper InstanceKlass!");
1993             }
1994 
1995             InstanceKlass kls = (InstanceKlass) obj;
1996             try {
1997                dumpKlass(kls);
1998                buf.append(".class created for ");
1999                buf.append(genKlassLink(kls));
2000             } catch(Exception exp) {
2001                buf.bold("can't .class for " +
2002                         genKlassTitle(kls) +
2003                         " : " +
2004                         exp.getMessage());
2005             }
2006             buf.br();
2007          }
2008 
2009          buf.genHTMLEpilogue();
2010          return buf.toString();
2011       } else {
2012          if (Assert.ASSERTS_ENABLED) {
2013             Assert.that(false, "unknown href link!");
2014          }
2015          return null;
2016       }
2017    }
2018 }