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