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