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