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