Skip to content

Commit

Permalink
Showing 13 changed files with 159 additions and 62 deletions.
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -265,6 +265,7 @@ public class Options {
public static final Option<Boolean> TRUFFLE_EXCEPTIONS_PRINT_UNCAUGHT_JAVA = bool(TRUFFLE, "truffle.exceptions.print_uncaught_java", false, "Print uncaught Java exceptions at the point of translating them to Ruby exceptions.");
public static final Option<Boolean> TRUFFLE_BACKTRACES_HIDE_CORE_FILES = bool(TRUFFLE, "truffle.backtraces.hide_core_files", true, "Hide core source files in backtraces, like MRI does.");
public static final Option<Integer> TRUFFLE_BACKTRACES_LIMIT = integer(TRUFFLE, "truffle.backtraces.limit", 9999, "Limit the size of Ruby backtraces.");
public static final Option<Boolean> TRUFFLE_BACKTRACES_OMIT_UNUSED = bool(TRUFFLE, "truffle.backtraces.omit_unused", true, "Omit backtraces that should be unused as they have pure rescue expressions.");
public static final Option<Boolean> TRUFFLE_INCLUDE_CORE_FILE_CALLERS_IN_SET_TRACE_FUNC = bool(TRUFFLE, "truffle.set_trace_func.include_core_file_callers", false, "Include internal core library calls in set_trace_func output.");

public static final Option<Boolean> TRUFFLE_METRICS_TIME = bool(TRUFFLE, "truffle.metrics.time", false, "Print the time at various stages of VM operation.");
Original file line number Diff line number Diff line change
@@ -118,7 +118,7 @@ public DynamicObject captureBacktrace(DynamicObject exception, NotProvided offse
@TruffleBoundary
@Specialization
public DynamicObject captureBacktrace(DynamicObject exception, int offset) {
Backtrace backtrace = RubyCallStack.getBacktrace(getContext(), this, offset);
Backtrace backtrace = RubyCallStack.getBacktrace(getContext(), this, offset, exception);
Layouts.EXCEPTION.setBacktrace(exception, backtrace);
return nil();
}
Original file line number Diff line number Diff line change
@@ -327,7 +327,7 @@ public DynamicObject callerLocations(int omit, NotProvided length) {
public DynamicObject callerLocations(int omit, int length) {
final DynamicObject threadBacktraceLocationClass = getContext().getCoreLibrary().getThreadBacktraceLocationClass();

final Backtrace backtrace = RubyCallStack.getBacktrace(getContext(), this, 1 + omit, true);
final Backtrace backtrace = RubyCallStack.getBacktrace(getContext(), this, 1 + omit, true, null);

int locationsCount = backtrace.getActivations().size();

Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ public DynamicObject absolutePath(DynamicObject threadBacktraceLocation) {
final Activation activation = ThreadBacktraceLocationLayoutImpl.INSTANCE.getActivation(threadBacktraceLocation);

if (activation.getCallNode() == null) {
return createString(StringOperations.encodeRope(BacktraceFormatter.OMITTED, UTF8Encoding.INSTANCE));
return createString(StringOperations.encodeRope(BacktraceFormatter.OMITTED_LIMIT, UTF8Encoding.INSTANCE));
}

final SourceSection sourceSection = activation.getCallNode().getEncapsulatingSourceSection();
@@ -90,7 +90,7 @@ public DynamicObject toS(DynamicObject threadBacktraceLocation) {
final Activation activation = ThreadBacktraceLocationLayoutImpl.INSTANCE.getActivation(threadBacktraceLocation);

if (activation.getCallNode() == null) {
return createString(StringOperations.encodeRope(BacktraceFormatter.OMITTED, UTF8Encoding.INSTANCE));
return createString(StringOperations.encodeRope(BacktraceFormatter.OMITTED_LIMIT, UTF8Encoding.INSTANCE));
}

final SourceSection sourceSection = activation.getCallNode().getEncapsulatingSourceSection();
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2015 Oracle and/or its affiliates. All rights reserved. This
* Copyright (c) 2013, 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
@@ -9,6 +9,7 @@
*/
package org.jruby.truffle.language.control;

import com.oracle.truffle.api.nodes.ControlFlowException;
import com.oracle.truffle.api.object.DynamicObject;

/**
@@ -17,9 +18,7 @@
* to throw them. The error messages match MRI. Note that throwing is different to raising in Ruby,
* which is the reason we have both {@link ThrowException} and {@link RaiseException}.
*/
public class RaiseException extends RuntimeException {

// TODO CS 1-Mar-15 shouldn't this be a ControlFlowException?
public class RaiseException extends ControlFlowException {

private final DynamicObject rubyException;

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.language.exceptions;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;

public class DisablingBacktracesNode extends RubyNode {

@Child private RubyNode child;

private static ThreadLocal<Boolean> areBacktracesDisabledThreadLocal = new ThreadLocal<Boolean>() {

@Override
protected Boolean initialValue() {
return false;
}
};

public DisablingBacktracesNode(RubyContext context, SourceSection sourceSection, RubyNode child) {
super(context, sourceSection);
this.child = child;
}

@Override
public Object execute(VirtualFrame frame) {
final boolean backtracesPreviouslyDisabled = areBacktracesDisabledThreadLocal.get();

try {
areBacktracesDisabledThreadLocal.set(true);

return child.execute(frame);
} finally {
areBacktracesDisabledThreadLocal.set(backtracesPreviouslyDisabled);
}
}

public static boolean areBacktracesDisabled() {
return areBacktracesDisabledThreadLocal.get();
}

}
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
import org.joni.NameEntry;
import org.joni.Regex;
import org.joni.Syntax;
import org.jruby.ast.SideEffectFree;
import org.jruby.ast.visitor.NodeVisitor;
import org.jruby.common.IRubyWarnings;
import org.jruby.lexer.yacc.InvalidSourcePosition;
@@ -28,6 +29,7 @@
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Visibility;
import org.jruby.truffle.language.control.*;
import org.jruby.truffle.language.exceptions.DisablingBacktracesNode;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.RubyRootNode;
import org.jruby.truffle.nodes.ThreadLocalObjectNode;
@@ -2667,59 +2669,78 @@ public RubyNode visitRescueNode(org.jruby.ast.RescueNode node) {

org.jruby.ast.RescueBodyNode rescueBody = node.getRescueNode();

while (rescueBody != null) {
if (rescueBody.getExceptionNodes() != null) {
if (rescueBody.getExceptionNodes() instanceof org.jruby.ast.ArrayNode) {
final org.jruby.ast.Node[] exceptionNodes = ((org.jruby.ast.ArrayNode) rescueBody.getExceptionNodes()).children();
if (context.getOptions().BACKTRACES_OMIT_UNUSED
&& rescueBody != null
&& rescueBody.getExceptionNodes() == null
&& rescueBody.getBodyNode() instanceof SideEffectFree
&& rescueBody.getOptRescueNode() == null) {
tryPart = new DisablingBacktracesNode(context, sourceSection, tryPart);

final RubyNode[] handlingClasses = new RubyNode[exceptionNodes.length];
RubyNode bodyNode;

for (int n = 0; n < handlingClasses.length; n++) {
handlingClasses[n] = exceptionNodes[n].accept(this);
}
if (rescueBody.getBodyNode() == null || rescueBody.getBodyNode().getPosition() == InvalidSourcePosition.INSTANCE) {
bodyNode = nilNode(sourceSection);
} else {
bodyNode = rescueBody.getBodyNode().accept(this);
}

RubyNode translatedBody;
final RescueAnyNode rescueNode = new RescueAnyNode(context, sourceSection, bodyNode);
rescueNodes.add(rescueNode);
} else {
while (rescueBody != null) {
if (rescueBody.getExceptionNodes() != null) {
if (rescueBody.getExceptionNodes() instanceof org.jruby.ast.ArrayNode) {
final org.jruby.ast.Node[] exceptionNodes = ((org.jruby.ast.ArrayNode) rescueBody.getExceptionNodes()).children();

if (rescueBody.getBodyNode() == null || rescueBody.getBodyNode().getPosition() == InvalidSourcePosition.INSTANCE) {
translatedBody = nilNode(sourceSection);
} else {
translatedBody = rescueBody.getBodyNode().accept(this);
}
final RubyNode[] handlingClasses = new RubyNode[exceptionNodes.length];

final RescueClassesNode rescueNode = new RescueClassesNode(context, sourceSection, handlingClasses, translatedBody);
rescueNodes.add(rescueNode);
} else if (rescueBody.getExceptionNodes() instanceof org.jruby.ast.SplatNode) {
final org.jruby.ast.SplatNode splat = (org.jruby.ast.SplatNode) rescueBody.getExceptionNodes();
for (int n = 0; n < handlingClasses.length; n++) {
handlingClasses[n] = exceptionNodes[n].accept(this);
}

RubyNode translatedBody;

if (rescueBody.getBodyNode() == null || rescueBody.getBodyNode().getPosition() == InvalidSourcePosition.INSTANCE) {
translatedBody = nilNode(sourceSection);
} else {
translatedBody = rescueBody.getBodyNode().accept(this);
}

final RescueClassesNode rescueNode = new RescueClassesNode(context, sourceSection, handlingClasses, translatedBody);
rescueNodes.add(rescueNode);
} else if (rescueBody.getExceptionNodes() instanceof org.jruby.ast.SplatNode) {
final org.jruby.ast.SplatNode splat = (org.jruby.ast.SplatNode) rescueBody.getExceptionNodes();

final RubyNode splatTranslated = translateNodeOrNil(sourceSection, splat.getValue());
final RubyNode splatTranslated = translateNodeOrNil(sourceSection, splat.getValue());

RubyNode bodyTranslated;
RubyNode bodyTranslated;

if (rescueBody.getBodyNode() == null || rescueBody.getBodyNode().getPosition() == InvalidSourcePosition.INSTANCE) {
bodyTranslated = nilNode(sourceSection);
} else {
bodyTranslated = rescueBody.getBodyNode().accept(this);
}

final RescueSplatNode rescueNode = new RescueSplatNode(context, sourceSection, splatTranslated, bodyTranslated);
rescueNodes.add(rescueNode);
} else {
unimplemented(node);
}
} else {
RubyNode bodyNode;

if (rescueBody.getBodyNode() == null || rescueBody.getBodyNode().getPosition() == InvalidSourcePosition.INSTANCE) {
bodyTranslated = nilNode(sourceSection);
bodyNode = nilNode(sourceSection);
} else {
bodyTranslated = rescueBody.getBodyNode().accept(this);
bodyNode = rescueBody.getBodyNode().accept(this);
}

final RescueSplatNode rescueNode = new RescueSplatNode(context, sourceSection, splatTranslated, bodyTranslated);
final RescueAnyNode rescueNode = new RescueAnyNode(context, sourceSection, bodyNode);
rescueNodes.add(rescueNode);
} else {
unimplemented(node);
}
} else {
RubyNode bodyNode;

if (rescueBody.getBodyNode() == null || rescueBody.getBodyNode().getPosition() == InvalidSourcePosition.INSTANCE) {
bodyNode = nilNode(sourceSection);
} else {
bodyNode = rescueBody.getBodyNode().accept(this);
}

final RescueAnyNode rescueNode = new RescueAnyNode(context, sourceSection, bodyNode);
rescueNodes.add(rescueNode);
rescueBody = rescueBody.getOptRescueNode();
}

rescueBody = rescueBody.getOptRescueNode();
}

RubyNode elsePart;
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ControlFlowException;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.utilities.BranchProfile;
import com.oracle.truffle.api.source.SourceSection;
@@ -50,9 +51,6 @@ public Object execute(VirtualFrame frame) {

try {
result = tryPart.execute(frame);
} catch (ControlFlowException exception) {
controlFlowProfile.enter();
throw exception;
} catch (RaiseException exception) {
raiseExceptionProfile.enter();

@@ -62,6 +60,9 @@ public Object execute(VirtualFrame frame) {
getContext().getSafepointManager().poll(this);
continue;
}
} catch (ControlFlowException exception) {
controlFlowProfile.enter();
throw exception;
}

elseProfile.enter();
@@ -74,9 +75,8 @@ public Object execute(VirtualFrame frame) {
}
}

@ExplodeLoop
private Object handleException(VirtualFrame frame, RaiseException exception) {
CompilerDirectives.transferToInterpreter();

for (RescueNode rescue : rescueParts) {
if (rescue.canHandle(frame, exception.getRubyException())) {
return setLastExceptionAndRunRescue(frame, exception, rescue);
Original file line number Diff line number Diff line change
@@ -55,12 +55,12 @@ public Object execute(VirtualFrame frame) {
} catch (TruffleFatalException | ThreadExitException exception) {
CompilerDirectives.transferToInterpreter();
throw exception;
} catch (ControlFlowException exception) {
controlProfile.enter();
throw exception;
} catch (RaiseException exception) {
rethrowProfile.enter();
throw exception;
} catch (ControlFlowException exception) {
controlProfile.enter();
throw exception;
} catch (MainExitException exception) {
CompilerDirectives.transferToInterpreter();
throw exception;
Original file line number Diff line number Diff line change
@@ -67,6 +67,7 @@ public class Options {
public final boolean EXCEPTIONS_PRINT_UNCAUGHT_JAVA = org.jruby.util.cli.Options.TRUFFLE_EXCEPTIONS_PRINT_UNCAUGHT_JAVA.load();
public final boolean BACKTRACES_HIDE_CORE_FILES = org.jruby.util.cli.Options.TRUFFLE_BACKTRACES_HIDE_CORE_FILES.load();
public final int BACKTRACES_LIMIT = org.jruby.util.cli.Options.TRUFFLE_BACKTRACES_LIMIT.load();
public final boolean BACKTRACES_OMIT_UNUSED = org.jruby.util.cli.Options.TRUFFLE_BACKTRACES_OMIT_UNUSED.load();
public final boolean INCLUDE_CORE_FILE_CALLERS_IN_SET_TRACE_FUNC = org.jruby.util.cli.Options.TRUFFLE_INCLUDE_CORE_FILE_CALLERS_IN_SET_TRACE_FUNC.load();

// Call garph
20 changes: 17 additions & 3 deletions truffle/src/main/java/org/jruby/truffle/runtime/RubyCallStack.java
Original file line number Diff line number Diff line change
@@ -15,10 +15,13 @@
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.FrameInstanceVisitor;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.language.arguments.RubyArguments;
import org.jruby.truffle.language.exceptions.DisablingBacktracesNode;
import org.jruby.truffle.runtime.backtrace.Activation;
import org.jruby.truffle.runtime.backtrace.Backtrace;
import org.jruby.truffle.core.CoreSourceSection;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.methods.InternalMethod;

import java.util.ArrayList;
@@ -57,12 +60,23 @@ public static Backtrace getBacktrace(RubyContext context, Node currentNode) {
}

public static Backtrace getBacktrace(RubyContext context, Node currentNode, int omit) {
return getBacktrace(context, currentNode, omit, false);
return getBacktrace(context, currentNode, omit, null);
}

public static Backtrace getBacktrace(RubyContext context, Node currentNode, final int omit, final boolean filterNullSourceSection) {
public static Backtrace getBacktrace(RubyContext context, Node currentNode, int omit, DynamicObject exception) {
return getBacktrace(context, currentNode, omit, false, exception);
}

public static Backtrace getBacktrace(RubyContext context, Node currentNode, final int omit, final boolean filterNullSourceSection, DynamicObject exception) {
CompilerAsserts.neverPartOfCompilation();

if (exception != null
&& context.getOptions().BACKTRACES_OMIT_UNUSED
&& DisablingBacktracesNode.areBacktracesDisabled()
&& ModuleOperations.assignableTo(Layouts.BASIC_OBJECT.getLogicalClass(exception), context.getCoreLibrary().getStandardErrorClass())) {
return new Backtrace(new Activation[]{Activation.OMITTED_UNUSED});
}

final int limit = context.getOptions().BACKTRACES_LIMIT;

final ArrayList<Activation> activations = new ArrayList<>();
@@ -83,7 +97,7 @@ public static Backtrace getBacktrace(RubyContext context, Node currentNode, fina
@Override
public Object visitFrame(FrameInstance frameInstance) {
if (depth > limit) {
activations.add(Activation.OMITTED);
activations.add(Activation.OMITTED_LIMIT);
return new Object();
}

Original file line number Diff line number Diff line change
@@ -14,7 +14,8 @@

public class Activation {

public static final Activation OMITTED = new Activation(null, null);
public static final Activation OMITTED_LIMIT = new Activation(null, null);
public static final Activation OMITTED_UNUSED = new Activation(null, null);

private final Node callNode;
private final MaterializedFrame materializedFrame;
Original file line number Diff line number Diff line change
@@ -29,7 +29,8 @@

public class BacktraceFormatter {

public static final String OMITTED = "(omitted due to -Xtruffle.backtraces.limit)";
public static final String OMITTED_LIMIT = "(omitted due to -Xtruffle.backtraces.limit)";
public static final String OMITTED_UNUSED = "(omitted as the rescue expression was pure; use -Xtruffle.backtraces.omit_for_unused=false to disable)";

public enum FormattingFlags {
OMIT_FROM_PREFIX,
@@ -106,8 +107,12 @@ private String formatInLine(List<Activation> activations, DynamicObject exceptio

final Activation activation = activations.get(0);

if (activation == Activation.OMITTED) {
return "(omitted due to -Xtruffle.backtraces.limit)";
if (activation == Activation.OMITTED_LIMIT) {
return OMITTED_LIMIT;
}

if (activation == Activation.OMITTED_UNUSED) {
return OMITTED_UNUSED;
}

final SourceSection sourceSection = activation.getCallNode().getEncapsulatingSourceSection();
@@ -169,8 +174,12 @@ private String formatFromLine(List<Activation> activations, int n) {
public String formatLine(List<Activation> activations, int n) {
final Activation activation = activations.get(n);

if (activation == Activation.OMITTED) {
return OMITTED;
if (activation == Activation.OMITTED_LIMIT) {
return OMITTED_LIMIT;
}

if (activation == Activation.OMITTED_UNUSED) {
return OMITTED_UNUSED;
}

final SourceSection sourceSection = activation.getCallNode().getEncapsulatingSourceSection();

0 comments on commit 832eff3

Please sign in to comment.