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