Skip to content

Commit

Permalink
Fix top-level bubbled exception rendering.
Browse files Browse the repository at this point in the history
Previous logic often caused OOM because of improve use of the
buffer and append logic in ThreadContext. New logic reuses
existing "raw" format logic avoiding one-off bugs. Output of a
bubbled exception now renders properly and looks like other "nice"
formatted exceptions (bug introduced for example):

[] ~/projects/jruby $ jruby -S rake spec:ruby:fast
Unhandled Java exception: java.lang.NullPointerException: null
java.lang.NullPointerException: null
             aryToAry at org/jruby/runtime/Helpers.java:1634
                toAry at org/jruby/runtime/IRBlockBody.java:109
              doYield at org/jruby/runtime/IRBlockBody.java:144
                yield at org/jruby/runtime/BlockBody.java:77
                yield at org/jruby/runtime/Block.java:147
          synchronize at org/jruby/ext/thread/Mutex.java:151
                 call at org/jruby/internal/runtime/methods/JavaMethod.java:494
         cacheAndCall at org/jruby/runtime/callsite/CachingCallSite.java:273
            callBlock at org/jruby/runtime/callsite/CachingCallSite.java:79
                 call at org/jruby/runtime/callsite/CachingCallSite.java:83
            interpret at org/jruby/ir/instructions/CallBase.java:419
          processCall at org/jruby/ir/interpreter/InterpreterEngine.java:322
            interpret at org/jruby/ir/interpreter/StartupInterpreterEngine.java:77
            interpret at org/jruby/ir/interpreter/InterpreterEngine.java:89
     INTERPRET_METHOD at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:232
                 call at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:218
                 call at org/jruby/internal/runtime/methods/DynamicMethod.java:205
         cacheAndCall at org/jruby/runtime/callsite/CachingCallSite.java:333
                 call at org/jruby/runtime/callsite/CachingCallSite.java:195
                <top> at /Users/headius/projects/jruby/bin/rake:22
  invokeWithArguments at java/lang/invoke/MethodHandle.java:627
                 load at org/jruby/ir/Compiler.java:111
            runScript at org/jruby/Ruby.java:820
            runScript at org/jruby/Ruby.java:812
          runNormally at org/jruby/Ruby.java:750
          runFromMain at org/jruby/Ruby.java:572
        doRunFromMain at org/jruby/Main.java:408
          internalRun at org/jruby/Main.java:303
                  run at org/jruby/Main.java:232
                 main at org/jruby/Main.java:201
headius committed Oct 8, 2015
1 parent f3dd725 commit ea8a70c
Showing 4 changed files with 33 additions and 31 deletions.
5 changes: 3 additions & 2 deletions core/src/main/java/org/jruby/Main.java
Original file line number Diff line number Diff line change
@@ -211,10 +211,11 @@ public static void main(String[] args) {
t.printStackTrace(System.err);
} else {
// print out as a nice Ruby backtrace
System.err.println(ThreadContext.createRawBacktraceStringFromThrowable(t));
System.err.println("Unhandled Java exception: " + t);
System.err.println(ThreadContext.createRawBacktraceStringFromThrowable(t, false));
while ((t = t.getCause()) != null) {
System.err.println("Caused by:");
System.err.println(ThreadContext.createRawBacktraceStringFromThrowable(t));
System.err.println(ThreadContext.createRawBacktraceStringFromThrowable(t, false));
}
}

26 changes: 6 additions & 20 deletions core/src/main/java/org/jruby/runtime/ThreadContext.java
Original file line number Diff line number Diff line change
@@ -744,30 +744,16 @@ public BacktraceElement[] createBacktrace2(int level, boolean nativeException) {
return newTrace;
}

private static StringBuilder appendRubyBacktraceString(final StringBuilder buffer, StackTraceElement element) {
return buffer.append( element.getFileName() ).append(':')
.append( element.getLineNumber() ).append(":in `")
.append( element.getMethodName() ).append('\'');
}

public static String createRawBacktraceStringFromThrowable(final Throwable ex) {
public static String createRawBacktraceStringFromThrowable(final Throwable ex, final boolean color) {
StackTraceElement[] javaStackTrace = ex.getStackTrace();

if (javaStackTrace == null || javaStackTrace.length == 0) return "";

final StringBuilder buffer = new StringBuilder(160);

StackTraceElement element = javaStackTrace[0];
buffer.append( appendRubyBacktraceString(buffer, element) )
.append(": ").append( ex.toString() );

for (int i = 1; i < javaStackTrace.length; i++) {
element = javaStackTrace[i];
buffer.append('\n');
buffer.append("\tfrom ").append( appendRubyBacktraceString(buffer, element) );
}

return buffer.toString();
return TraceType.printBacktraceJRuby(
new BacktraceData(javaStackTrace, new BacktraceElement[0], true, false, false).getBacktraceWithoutRuby(),
ex.getClass().getName(),
ex.getLocalizedMessage(),
color);
}

private Frame pushFrameForBlock(Binding binding) {
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;

public class BacktraceData implements Serializable {
@@ -37,6 +38,10 @@ public RubyStackTraceElement[] getBacktrace(Ruby runtime) {
return backtraceElements;
}

public RubyStackTraceElement[] getBacktraceWithoutRuby() {
return constructBacktrace(Collections.EMPTY_MAP);
}

private RubyStackTraceElement[] constructBacktrace(Map<String, Map<String, String>> boundMethods) {
ArrayList<RubyStackTraceElement> trace = new ArrayList<RubyStackTraceElement>(javaTrace.length);

28 changes: 19 additions & 9 deletions core/src/main/java/org/jruby/runtime/backtrace/TraceType.java
Original file line number Diff line number Diff line change
@@ -320,30 +320,40 @@ protected static String printBacktraceMRI(RubyException exception, boolean conso
private static final String EVAL_COLOR = "\033[0;33m";
private static final String CLEAR_COLOR = "\033[0m";

public static String printBacktraceJRuby(RubyStackTraceElement[] frames, String type, String message, boolean color) {
StringBuilder buffer = new StringBuilder();

// exception line
buffer
.append(type)
.append(": ")
.append(message)
.append('\n');

if (frames == null) frames = RubyStackTraceElement.EMPTY_ARRAY;
renderBacktraceJRuby(frames, buffer, color);


return buffer.toString();
}

protected static String printBacktraceJRuby(RubyException exception, boolean console) {
final Ruby runtime = exception.getRuntime();
final ThreadContext context = runtime.getCurrentContext();

StringBuilder buffer = new StringBuilder();
boolean color = console && runtime.getInstanceConfig().getBacktraceColor();

// exception line
String message = exception.message(context).toString();
if (exception.getMetaClass() == runtime.getRuntimeError() && message.length() == 0) {
message = "No current exception";
}
buffer
.append(exception.getMetaClass().getName())
.append(": ")
.append(message)
.append('\n');
String type = exception.getMetaClass().getName();

RubyStackTraceElement[] frames = exception.getBacktraceElements();
if (frames == null) frames = RubyStackTraceElement.EMPTY_ARRAY;
renderBacktraceJRuby(frames, buffer, color);


return buffer.toString();
return printBacktraceJRuby(frames, type, message, color);
}

private static void renderBacktraceJRuby(RubyStackTraceElement[] frames, StringBuilder buffer, boolean color) {

0 comments on commit ea8a70c

Please sign in to comment.