001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.jikesrvm.runtime; 014 015import org.jikesrvm.VM; 016import org.jikesrvm.architecture.AbstractRegisters; 017import org.jikesrvm.architecture.StackFrameLayout; 018import org.jikesrvm.Options; 019import org.jikesrvm.classloader.Atom; 020import org.jikesrvm.classloader.MemberReference; 021import org.jikesrvm.classloader.RVMMethod; 022import org.jikesrvm.classloader.NormalMethod; 023import org.jikesrvm.compilers.baseline.BaselineCompiledMethod; 024import org.jikesrvm.compilers.common.CompiledMethod; 025import org.jikesrvm.compilers.common.CompiledMethods; 026import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod; 027import org.jikesrvm.compilers.opt.runtimesupport.OptEncodedCallSiteTree; 028import org.jikesrvm.compilers.opt.runtimesupport.OptMachineCodeMap; 029import org.jikesrvm.scheduler.RVMThread; 030import org.vmmagic.pragma.Uninterruptible; 031import org.vmmagic.pragma.NoInline; 032import org.vmmagic.unboxed.Address; 033import org.vmmagic.unboxed.Offset; 034 035/** 036 * A list of compiled method and instructionOffset pairs that describe the state 037 * of the call stack at a particular instant. 038 */ 039public class StackTrace { 040 041 /** 042 * Prints an internal stack trace for every stack trace obtained via 043 * {@link #getStackTrace(Throwable)}. The internal stack trace 044 * has machine code offsets and bytecode index information for methods. 045 */ 046 private static final boolean PRINT_INTERNAL_STACK_TRACE = false; 047 048 /** 049 * The compiled method ids of the stack trace. Ordered with the top of the stack at 050 * 0 and the bottom of the stack at the end of the array 051 */ 052 private final int[] compiledMethods; 053 054 /** The offset of the instruction within the compiled method */ 055 private final int[] instructionOffsets; 056 057 /** Index of the last stack trace; only used to support VM.VerboseStackTracePeriod */ 058 private static int lastTraceIndex = 0; 059 060 /** 061 * Create a trace for the call stack of the current thread 062 */ 063 @NoInline 064 public StackTrace() { 065 this(RVMThread.getCurrentThread()); 066 } 067 068 /** 069 * Constructs a stack trace. 070 * <p> 071 * Note: no inlining directives here because they aren't necessary for correctness. 072 * This class removes all frames belonging to its methods. 073 * 074 * @param rvmThread the thread whose stack is examined. It is the caller's 075 * responsibility to block that thread if required. 076 */ 077 public StackTrace(RVMThread rvmThread) { 078 assertThreadBlockedOrCurrent(rvmThread); 079 boolean isVerbose = false; 080 int traceIndex = 0; 081 if (VM.VerifyAssertions && VM.VerboseStackTracePeriod > 0) { 082 // Poor man's atomic integer, to get through bootstrap 083 synchronized (StackTrace.class) { 084 traceIndex = lastTraceIndex++; 085 } 086 isVerbose = (traceIndex % VM.VerboseStackTracePeriod == 0); 087 } 088 // (1) Count the number of frames comprising the stack. 089 int numFrames = countFramesUninterruptible(rvmThread); 090 // (2) Construct arrays to hold raw data 091 compiledMethods = new int[numFrames]; 092 instructionOffsets = new int[numFrames]; 093 // (3) Fill in arrays 094 recordFramesUninterruptible(rvmThread); 095 // Debugging trick: print every nth stack trace created 096 if (isVerbose) { 097 VM.disableGC(); 098 VM.sysWriteln("[ BEGIN Verbosely dumping stack at time of creating StackTrace # ", traceIndex); 099 RVMThread.dumpStack(); 100 VM.sysWriteln("END Verbosely dumping stack at time of creating StackTrace # ", traceIndex, " ]"); 101 VM.enableGC(); 102 } 103 } 104 105 private void assertThreadBlockedOrCurrent(RVMThread rvmThread) { 106 if (VM.VerifyAssertions) { 107 if (rvmThread != RVMThread.getCurrentThread()) { 108 rvmThread.monitor().lockNoHandshake(); 109 boolean blocked = rvmThread.isBlocked(); 110 rvmThread.monitor().unlock(); 111 if (!blocked) { 112 String msg = "Can only dump stack of blocked threads if not dumping" + 113 " own stack but thread " + rvmThread + " was in state " + 114 rvmThread.getExecStatus() + " and wasn't blocked!"; 115 VM._assert(VM.NOT_REACHED, msg); 116 } 117 } 118 } 119 } 120 121 /** 122 * Walk the stack counting the number of stack frames encountered. 123 * The stack being walked is our stack, so code is Uninterruptible to stop the 124 * stack moving. 125 * 126 * @param stackTraceThread the thread whose stack is walked 127 * @return number of stack frames encountered 128 */ 129 @Uninterruptible 130 @NoInline 131 private int countFramesUninterruptible(RVMThread stackTraceThread) { 132 int stackFrameCount = 0; 133 Address fp; 134 /* Stack trace for the thread */ 135 if (stackTraceThread == RVMThread.getCurrentThread()) { 136 fp = Magic.getFramePointer(); 137 } else { 138 AbstractRegisters contextRegisters = stackTraceThread.getContextRegisters(); 139 fp = contextRegisters.getInnermostFramePointer(); 140 } 141 fp = Magic.getCallerFramePointer(fp); 142 while (Magic.getCallerFramePointer(fp).NE(StackFrameLayout.getStackFrameSentinelFP())) { 143 int compiledMethodId = Magic.getCompiledMethodID(fp); 144 if (compiledMethodId != StackFrameLayout.getInvisibleMethodID()) { 145 CompiledMethod compiledMethod = 146 CompiledMethods.getCompiledMethod(compiledMethodId); 147 if ((compiledMethod.getCompilerType() != CompiledMethod.TRAP) && 148 compiledMethod.hasBridgeFromNativeAnnotation()) { 149 // skip native frames, stopping at last native frame preceeding the 150 // Java To C transition frame 151 fp = RuntimeEntrypoints.unwindNativeStackFrame(fp); 152 } 153 } 154 stackFrameCount++; 155 fp = Magic.getCallerFramePointer(fp); 156 } 157 //VM.sysWriteln("stack frame count = ",stackFrameCount); 158 return stackFrameCount; 159 } 160 161 /** 162 * Walk the stack recording the stack frames encountered 163 * The stack being walked is our stack, so code is Uninterrupible to stop the 164 * stack moving. 165 * 166 * @param stackTraceThread the thread whose stack is walked 167 */ 168 @Uninterruptible 169 @NoInline 170 private void recordFramesUninterruptible(RVMThread stackTraceThread) { 171 int stackFrameCount = 0; 172 Address fp; 173 Address ip; 174 /* Stack trace for the thread */ 175 if (stackTraceThread == RVMThread.getCurrentThread()) { 176 fp = Magic.getFramePointer(); 177 } else { 178 AbstractRegisters contextRegisters = stackTraceThread.getContextRegisters(); 179 fp = contextRegisters.getInnermostFramePointer(); 180 } 181 ip = Magic.getReturnAddress(fp); 182 fp = Magic.getCallerFramePointer(fp); 183 while (Magic.getCallerFramePointer(fp).NE(StackFrameLayout.getStackFrameSentinelFP())) { 184 //VM.sysWriteln("at stackFrameCount = ",stackFrameCount); 185 int compiledMethodId = Magic.getCompiledMethodID(fp); 186 compiledMethods[stackFrameCount] = compiledMethodId; 187 if (compiledMethodId != StackFrameLayout.getInvisibleMethodID()) { 188 CompiledMethod compiledMethod = 189 CompiledMethods.getCompiledMethod(compiledMethodId); 190 if (compiledMethod.getCompilerType() != CompiledMethod.TRAP) { 191 instructionOffsets[stackFrameCount] = 192 compiledMethod.getInstructionOffset(ip).toInt(); 193 if (compiledMethod.hasBridgeFromNativeAnnotation()) { 194 //VM.sysWriteln("native!"); 195 // skip native frames, stopping at last native frame preceeding the 196 // Java To C transition frame 197 fp = RuntimeEntrypoints.unwindNativeStackFrame(fp); 198 } 199 } else { 200 //VM.sysWriteln("trap!"); 201 } 202 } else { 203 //VM.sysWriteln("invisible method!"); 204 } 205 stackFrameCount++; 206 ip = Magic.getReturnAddress(fp, stackTraceThread); 207 fp = Magic.getCallerFramePointer(fp); 208 } 209 } 210 211 /** Class to wrap up a stack frame element */ 212 public static class Element { 213 /** Stack trace's method, null => invisible or trap */ 214 protected final RVMMethod method; 215 /** Line number of element */ 216 protected final int lineNumber; 217 /** Is this an invisible method? */ 218 protected final boolean isInvisible; 219 /** Is this a hardware trap method? */ 220 protected final boolean isTrap; 221 222 /** 223 * Constructor for non-opt compiled methods 224 * @param cm the compiled method 225 * @param off offset of the instruction from start of machine code, 226 * in bytes 227 */ 228 Element(CompiledMethod cm, int off) { 229 isInvisible = (cm == null); 230 if (!isInvisible) { 231 isTrap = cm.getCompilerType() == CompiledMethod.TRAP; 232 if (!isTrap) { 233 method = cm.getMethod(); 234 lineNumber = cm.findLineNumberForInstruction(Offset.fromIntSignExtend(off)); 235 } else { 236 method = null; 237 lineNumber = 0; 238 } 239 } else { 240 isTrap = false; 241 method = null; 242 lineNumber = 0; 243 } 244 } 245 246 /** 247 * Constructor for opt compiled methods. 248 * @param method the method that was called 249 * @param ln the line number 250 */ 251 Element(RVMMethod method, int ln) { 252 this.method = method; 253 lineNumber = ln; 254 isTrap = false; 255 isInvisible = false; 256 } 257 258 /** @return source file name */ 259 public String getFileName() { 260 if (isInvisible || isTrap) { 261 return null; 262 } else { 263 Atom fn = method.getDeclaringClass().getSourceName(); 264 return (fn != null) ? fn.toString() : null; 265 } 266 } 267 268 public String getClassName() { 269 if (isInvisible || isTrap) { 270 return ""; 271 } else { 272 return method.getDeclaringClass().toString(); 273 } 274 } 275 276 public Class<?> getElementClass() { 277 if (isInvisible || isTrap) { 278 return null; 279 } 280 return method.getDeclaringClass().getClassForType(); 281 } 282 283 public String getMethodName() { 284 if (isInvisible) { 285 return "<invisible method>"; 286 } else if (isTrap) { 287 return "<hardware trap>"; 288 } else { 289 if (method != null) { 290 return method.getName().toString(); 291 } else { 292 return "<unknown method: method was null>"; 293 } 294 } 295 } 296 297 public int getLineNumber() { 298 return lineNumber; 299 } 300 301 public boolean isNative() { 302 if (isInvisible || isTrap) { 303 return false; 304 } else { 305 return method.isNative(); 306 } 307 } 308 } 309 310 /** 311 * A stack trace element that contains additional debugging information, 312 * namely machine code offsets and byte code indexes. 313 */ 314 static class InternalStackTraceElement extends Element { 315 /** machine code offset */ 316 private Offset mcOffset; 317 /** byte code index */ 318 private int bci; 319 320 /** 321 * Constructor for non-opt compiled methods 322 * @param cm the compiled method 323 * @param off offset of the instruction from start of machine code, 324 * in bytes 325 */ 326 InternalStackTraceElement(CompiledMethod cm, int off) { 327 super(cm, off); 328 if (!isInvisible) { 329 if (!isTrap) { 330 Offset machineCodeOffset = Offset.fromIntSignExtend(off); 331 mcOffset = machineCodeOffset; 332 if (cm instanceof BaselineCompiledMethod) { 333 bci = ((BaselineCompiledMethod) cm).findBytecodeIndexForInstruction(machineCodeOffset); 334 } else if (cm instanceof OptCompiledMethod) { 335 bci = ((OptCompiledMethod) cm).getMCMap().getBytecodeIndexForMCOffset(machineCodeOffset); 336 } else { 337 bci = 0; 338 } 339 } else { 340 mcOffset = Offset.zero(); 341 bci = 0; 342 } 343 } else { 344 mcOffset = Offset.zero(); 345 bci = 0; 346 } 347 } 348 349 /** 350 * Constructor for opt compiled methods. 351 * @param method the method that was called 352 * @param ln the line number 353 * @param mcOffset the machine code offset for the line 354 * @param bci the bytecode index for the line 355 * 356 */ 357 InternalStackTraceElement(RVMMethod method, int ln, Offset mcOffset, int bci) { 358 super(method, ln); 359 this.mcOffset = mcOffset; 360 this.bci = bci; 361 } 362 363 void printForDebugging() { 364 VM.sysWrite("{IST: "); 365 VM.sysWrite(method.getDeclaringClass().toString()); 366 VM.sysWrite("."); 367 VM.sysWrite(method.getName()); 368 VM.sysWrite(" --- "); 369 VM.sysWrite("line_number: "); 370 VM.sysWrite(lineNumber); 371 VM.sysWrite(" byte_code_index: "); 372 VM.sysWrite(bci); 373 VM.sysWrite(" machine_code_offset: "); 374 VM.sysWrite(mcOffset); 375 VM.sysWriteln(); 376 } 377 } 378 379 private CompiledMethod getCompiledMethod(int element) { 380 if ((element >= 0) && (element < compiledMethods.length)) { 381 int mid = compiledMethods[element]; 382 if (mid != StackFrameLayout.getInvisibleMethodID()) { 383 return CompiledMethods.getCompiledMethod(mid); 384 } 385 } 386 return null; 387 } 388 389 /** 390 * @param cause the throwable that caused the stack trace 391 * @return the stack trace for use by the Throwable API 392 */ 393 public Element[] getStackTrace(Throwable cause) { 394 int first = firstRealMethod(cause); 395 int last = lastRealMethod(first); 396 Element[] elements = buildStackTrace(first, last); 397 if (PRINT_INTERNAL_STACK_TRACE) { 398 VM.sysWriteln(); 399 for (Element e : elements) { 400 InternalStackTraceElement internalEle = (InternalStackTraceElement) e; 401 internalEle.printForDebugging(); 402 } 403 } 404 return elements; 405 } 406 407 private Element createStandardStackTraceElement(CompiledMethod cm, int off) { 408 if (!PRINT_INTERNAL_STACK_TRACE) { 409 return new Element(cm, off); 410 } else { 411 return new InternalStackTraceElement(cm, off); 412 } 413 } 414 415 private Element createOptStackTraceElement(RVMMethod m, int ln, Offset mcOffset, int bci) { 416 if (!PRINT_INTERNAL_STACK_TRACE) { 417 return new Element(m, ln); 418 } else { 419 return new InternalStackTraceElement(m, ln, mcOffset, bci); 420 } 421 } 422 423 private Element[] buildStackTrace(int first, int last) { 424 Element[] elements = new Element[countFrames(first, last)]; 425 if (!VM.BuildForOptCompiler) { 426 int element = 0; 427 for (int i = first; i <= last; i++) { 428 elements[element] = createStandardStackTraceElement(getCompiledMethod(i), instructionOffsets[i]); 429 element++; 430 } 431 } else { 432 int element = 0; 433 for (int i = first; i <= last; i++) { 434 CompiledMethod compiledMethod = getCompiledMethod(i); 435 if ((compiledMethod == null) || 436 (compiledMethod.getCompilerType() != CompiledMethod.OPT)) { 437 // Invisible or non-opt compiled method 438 elements[element] = createStandardStackTraceElement(compiledMethod, instructionOffsets[i]); 439 element++; 440 } else { 441 Offset instructionOffset = Offset.fromIntSignExtend(instructionOffsets[i]); 442 OptCompiledMethod optInfo = (OptCompiledMethod)compiledMethod; 443 OptMachineCodeMap map = optInfo.getMCMap(); 444 int iei = map.getInlineEncodingForMCOffset(instructionOffset); 445 if (iei < 0) { 446 elements[element] = createStandardStackTraceElement(compiledMethod, instructionOffsets[i]); 447 element++; 448 } else { 449 int[] inlineEncoding = map.inlineEncoding; 450 int bci = map.getBytecodeIndexForMCOffset(instructionOffset); 451 for (; iei >= 0; iei = OptEncodedCallSiteTree.getParent(iei, inlineEncoding)) { 452 int mid = OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding); 453 RVMMethod method = MemberReference.getMethodRef(mid).getResolvedMember(); 454 int lineNumber = ((NormalMethod)method).getLineNumberForBCIndex(bci); 455 elements[element] = createOptStackTraceElement(method, lineNumber, instructionOffset, bci); 456 element++; 457 if (iei > 0) { 458 bci = OptEncodedCallSiteTree.getByteCodeOffset(iei, inlineEncoding); 459 } 460 } 461 } 462 } 463 } 464 } 465 return elements; 466 } 467 468 /** 469 * Count number of stack frames including those inlined by the opt compiler 470 * @param first the first compiled method to look from 471 * @param last the last compiled method to look to 472 * @return the number of stack frames 473 */ 474 private int countFrames(int first, int last) { 475 int numElements = 0; 476 if (!VM.BuildForOptCompiler) { 477 numElements = last - first + 1; 478 } else { 479 for (int i = first; i <= last; i++) { 480 CompiledMethod compiledMethod = getCompiledMethod(i); 481 if ((compiledMethod == null) || 482 (compiledMethod.getCompilerType() != CompiledMethod.OPT)) { 483 // Invisible or non-opt compiled method 484 numElements++; 485 } else { 486 Offset instructionOffset = Offset.fromIntSignExtend(instructionOffsets[i]); 487 OptCompiledMethod optInfo = (OptCompiledMethod)compiledMethod; 488 OptMachineCodeMap map = optInfo.getMCMap(); 489 int iei = map.getInlineEncodingForMCOffset(instructionOffset); 490 if (iei < 0) { 491 numElements++; 492 } else { 493 int[] inlineEncoding = map.inlineEncoding; 494 for (; iei >= 0; iei = OptEncodedCallSiteTree.getParent(iei, inlineEncoding)) { 495 numElements++; 496 } 497 } 498 } 499 } 500 } 501 return numElements; 502 } 503 504 /** 505 * Find the first non-VM method/exception initializer method in the stack 506 * trace. As we're working with the compiled methods we're assuming the 507 * constructor of the exception won't have been inlined into the throwing 508 * method. 509 * 510 * @param cause the cause of generating the stack trace marking the end of the 511 * frames to elide 512 * @return the index of the method throwing the exception or else 0 513 */ 514 private int firstRealMethod(Throwable cause) { 515 /* We expect a hardware trap to look like: 516 * at org.jikesrvm.runtime.StackTrace.<init>(StackTrace.java:78) 517 * at java.lang.VMThrowable.fillInStackTrace(VMThrowable.java:67) 518 * at java.lang.Throwable.fillInStackTrace(Throwable.java:498) 519 * at java.lang.Throwable.<init>(Throwable.java:159) 520 * at java.lang.Throwable.<init>(Throwable.java:147) 521 * at java.lang.Exception.<init>(Exception.java:66) 522 * at java.lang.RuntimeException.<init>(RuntimeException.java:64) 523 * at java.lang.NullPointerException.<init>(NullPointerException.java:69) 524 * at org.jikesrvm.runtime.RuntimeEntrypoints.deliverHardwareException(RuntimeEntrypoints.java:682) 525 * at <hardware trap>(Unknown Source:0) 526 * 527 * and a software trap to look like: 528 * at org.jikesrvm.runtime.StackTrace.<init>(StackTrace.java:78) 529 * at java.lang.VMThrowable.fillInStackTrace(VMThrowable.java:67) 530 * at java.lang.Throwable.fillInStackTrace(Throwable.java:498) 531 * at java.lang.Throwable.<init>(Throwable.java:159) 532 * at java.lang.Error.<init>(Error.java:81) 533 * at java.lang.LinkageError.<init>(LinkageError.java:72) 534 * at java.lang.ExceptionInInitializerError.<init>(ExceptionInInitializerError.java:85) 535 * at java.lang.ExceptionInInitializerError.<init>(ExceptionInInitializerError.java:75) 536 * 537 * and an OutOfMemoryError to look like: 538 * ??? 539 * ... 540 * at org.jikesrvm.mm.mminterface.MemoryManager.allocateSpace(MemoryManager.java:613) 541 * ... 542 * at org.jikesrvm.runtime.RuntimeEntrypoints.unresolvedNewArray(RuntimeEntrypoints.java:401) 543 */ 544 if (Options.stackTraceFull) { 545 return 0; 546 } else { 547 int element = 0; 548 CompiledMethod compiledMethod = getCompiledMethod(element); 549 550 // Deal with OutOfMemoryError 551 if (cause instanceof OutOfMemoryError) { 552 // (1) search until RuntimeEntrypoints 553 while ((element < compiledMethods.length) && 554 (compiledMethod != null) && 555 compiledMethod.getMethod().getDeclaringClass().getClassForType() != RuntimeEntrypoints.class) { 556 element++; 557 compiledMethod = getCompiledMethod(element); 558 } 559 // (2) continue until not RuntimeEntrypoints 560 while ((element < compiledMethods.length) && 561 (compiledMethod != null) && 562 compiledMethod.getMethod().getDeclaringClass().getClassForType() == RuntimeEntrypoints.class) { 563 element++; 564 compiledMethod = getCompiledMethod(element); 565 } 566 return element; 567 } 568 569 // (1) remove any StackTrace frames 570 element = removeStackTraceFrames(element); 571 compiledMethod = getCompiledMethod(element); 572 573 // (2) remove any VMThrowable frames 574 if (VM.BuildForGnuClasspath) { 575 while ((element < compiledMethods.length) && 576 (compiledMethod != null) && 577 compiledMethod.getMethod().getDeclaringClass().getClassForType().getName().equals("java.lang.VMThrowable")) { 578 element++; 579 compiledMethod = getCompiledMethod(element); 580 } 581 } 582 // (3) remove any Throwable frames 583 while ((element < compiledMethods.length) && 584 (compiledMethod != null) && 585 compiledMethod.getMethod().getDeclaringClass().getClassForType() == java.lang.Throwable.class) { 586 element++; 587 compiledMethod = getCompiledMethod(element); 588 } 589 // (4) remove frames belonging to exception constructors upto the causes constructor 590 while ((element < compiledMethods.length) && 591 (compiledMethod != null) && 592 (compiledMethod.getMethod().getDeclaringClass().getClassForType() != cause.getClass()) && 593 compiledMethod.getMethod().isObjectInitializer() && 594 compiledMethod.getMethod().getDeclaringClass().isThrowable()) { 595 element++; 596 compiledMethod = getCompiledMethod(element); 597 } 598 // (5) remove frames belonging to the causes constructor 599 // NB This can be made to incorrectly elide frames if the cause 600 // exception is thrown from a constructor of the cause exception, however, 601 // Sun's VM has the same problem 602 while ((element < compiledMethods.length) && 603 (compiledMethod != null) && 604 (compiledMethod.getMethod().getDeclaringClass().getClassForType() == cause.getClass()) && 605 compiledMethod.getMethod().isObjectInitializer()) { 606 element++; 607 compiledMethod = getCompiledMethod(element); 608 } 609 // (6) remove possible hardware exception deliverer frames 610 if (element < compiledMethods.length - 2) { 611 compiledMethod = getCompiledMethod(element + 1); 612 if ((compiledMethod != null) && 613 compiledMethod.getCompilerType() == CompiledMethod.TRAP) { 614 element += 2; 615 } 616 } 617 return element; 618 } 619 } 620 621 /** 622 * Finds the first non-VM method in the stack trace. In this case, the assumption 623 * is that no exception occurred which makes the job of this method much easier 624 * than of {@link #firstRealMethod(Throwable)}: it is only necessary to skip 625 * frames from this class. 626 * 627 * @return the index of the method or else 0 628 */ 629 private int firstRealMethod() { 630 return removeStackTraceFrames(0); 631 } 632 633 /** 634 * Removes all frames from the StackTrace class (i.e. this class) from 635 * the stack trace by skipping them. 636 * <p> 637 * Note: Callers must update all data relating to the element index themselves. 638 * 639 * @param element the element index 640 * @return an updated element index 641 */ 642 private int removeStackTraceFrames(int element) { 643 CompiledMethod compiledMethod = getCompiledMethod(element); 644 while ((element < compiledMethods.length) && 645 (compiledMethod != null) && 646 compiledMethod.getMethod().getDeclaringClass().getClassForType() == StackTrace.class) { 647 element++; 648 compiledMethod = getCompiledMethod(element); 649 } 650 return element; 651 } 652 653 /** 654 * Find the first non-VM method at the end of the stack trace 655 * @param first the first real method of the stack trace 656 * @return compiledMethods.length-1 if no non-VM methods found else the index of 657 * the method 658 */ 659 private int lastRealMethod(int first) { 660 /* We expect an exception on the main thread to look like: 661 * at <invisible method>(Unknown Source:0) 662 * at org.jikesrvm.runtime.Reflection.invoke(Reflection.java:132) 663 * at org.jikesrvm.scheduler.MainThread.run(MainThread.java:195) 664 * at org.jikesrvm.scheduler.RVMThread.run(RVMThread.java:534) 665 * at org.jikesrvm.scheduler.RVMThread.startoff(RVMThread.java:1113 666 * 667 * and on another thread to look like: 668 * at org.jikesrvm.scheduler.RVMThread.run(RVMThread.java:534) 669 * at org.jikesrvm.scheduler.RVMThread.startoff(RVMThread.java:1113) 670 */ 671 int max = compiledMethods.length - 1; 672 if (Options.stackTraceFull) { 673 return max; 674 } else { 675 // Start at end of array and elide a frame unless we find a place to stop 676 for (int i = max; i >= first; i--) { 677 if (compiledMethods[i] == StackFrameLayout.getInvisibleMethodID()) { 678 // we found an invisible method, assume next method if this is sane 679 if (i - 1 >= 0) { 680 return i - 1; 681 } else { 682 return max; // not sane => return max 683 } 684 } 685 CompiledMethod compiledMethod = getCompiledMethod(i); 686 if (compiledMethod.getCompilerType() == CompiledMethod.TRAP) { 687 // looks like we've gone too low 688 return max; 689 } 690 Class<?> frameClass = compiledMethod.getMethod().getDeclaringClass().getClassForType(); 691 if ((frameClass != org.jikesrvm.scheduler.MainThread.class) && 692 (frameClass != org.jikesrvm.scheduler.RVMThread.class) && 693 (frameClass != org.jikesrvm.runtime.Reflection.class)) { 694 // Found a non-VM method 695 return i; 696 } 697 } 698 // No frame found 699 return max; 700 } 701 } 702 703 /** 704 * Gets a stack trace at the current point in time, assuming the thread didn't 705 * throw any exception. 706 * 707 * @param framesToSkip count of frames to skip. Note: frames from this class are always 708 * skipped and thus not included in the count. For example, if the caller were 709 * {@code foo()} and you wanted to skip {@code foo}'s frame, you would pass 710 * {@code 1}. 711 * 712 * @return a stack trace 713 */ 714 public Element[] stackTraceNoException(int framesToSkip) { 715 if (VM.VerifyAssertions) VM._assert(framesToSkip >= 0, "Cannot skip negative amount of frames"); 716 int first = firstRealMethod(); 717 first += framesToSkip; 718 int last = lastRealMethod(first); 719 return buildStackTrace(first, last); 720 } 721 722} 723