Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 797aea450747
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 37c16a20fbf1
Choose a head ref
  • 14 commits
  • 21 files changed
  • 2 contributors

Commits on Mar 16, 2016

  1. Copy the full SHA
    6d4af5a View commit details
  2. Copy the full SHA
    8c35779 View commit details
  3. Copy the full SHA
    6c1411a View commit details
  4. Copy the full SHA
    f77ee64 View commit details
  5. Copy the full SHA
    9111603 View commit details
  6. Copy the full SHA
    daa3572 View commit details
  7. Copy the full SHA
    7099373 View commit details
  8. Copy the full SHA
    b7ecbb2 View commit details
  9. Copy the full SHA
    cad6f61 View commit details
  10. Copy the full SHA
    2d31429 View commit details
  11. Copy the full SHA
    401c4ef View commit details
  12. Copy the full SHA
    de6d395 View commit details
  13. 4
    Copy the full SHA
    d1c95c2 View commit details
  14. Copy the full SHA
    37c16a2 View commit details
Showing with 620 additions and 179 deletions.
  1. +0 −1 core/src/main/java/org/jruby/util/cli/Options.java
  2. +3 −3 lib/ruby/truffle/jruby+truffle/lib/runner.rb
  3. +4 −4 test/truffle/compiler/attachments-optimise/attachments-optimise.rb
  4. +3 −0 test/truffle/compiler/stf-optimises.sh
  5. +40 −0 test/truffle/compiler/stf-optimises/stf-optimises.rb
  6. +3 −0 test/truffle/compiler/tp-optimises.sh
  7. +40 −0 test/truffle/compiler/tp-optimises/tp-optimises.rb
  8. +5 −0 truffle/src/main/java/org/jruby/truffle/RubyContext.java
  9. +4 −0 truffle/src/main/java/org/jruby/truffle/core/CoreLibrary.java
  10. +3 −0 truffle/src/main/java/org/jruby/truffle/core/Layouts.java
  11. +74 −71 truffle/src/main/java/org/jruby/truffle/core/kernel/TraceManager.java
  12. +11 −0 truffle/src/main/java/org/jruby/truffle/core/rope/RopeTable.java
  13. +14 −7 truffle/src/main/java/org/jruby/truffle/core/string/CoreString.java
  14. +6 −0 truffle/src/main/java/org/jruby/truffle/core/string/CoreStrings.java
  15. +87 −0 truffle/src/main/java/org/jruby/truffle/core/tracepoint/TracePointEventNode.java
  16. +62 −0 truffle/src/main/java/org/jruby/truffle/core/tracepoint/TracePointLayout.java
  17. +229 −0 truffle/src/main/java/org/jruby/truffle/core/tracepoint/TracePointNodes.java
  18. +0 −2 truffle/src/main/java/org/jruby/truffle/language/Options.java
  19. +4 −0 truffle/src/main/java/org/jruby/truffle/language/RubyGuards.java
  20. +27 −90 truffle/src/main/java/org/jruby/truffle/language/backtrace/BacktraceFormatter.java
  21. +1 −1 truffle/src/main/java/org/jruby/truffle/tools/simpleshell/SimpleShell.java
1 change: 0 additions & 1 deletion core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -271,7 +271,6 @@ public class Options {
public static final Option<Boolean> TRUFFLE_BACKTRACES_INTERLEAVE_JAVA = bool(TRUFFLE, "truffle.backtraces.interleave_java", false, "Interleave Java stacktraces into the Ruby backtrace.");
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.");
public static final Option<Boolean> TRUFFLE_METRICS_MEMORY_USED_ON_EXIT = bool(TRUFFLE, "truffle.metrics.memory_used_on_exit", false, "Print the size of heap memory in use on exit.");
6 changes: 3 additions & 3 deletions lib/ruby/truffle/jruby+truffle/lib/runner.rb
Original file line number Diff line number Diff line change
@@ -151,7 +151,7 @@ module OptionBlocks
readme: {
help: ['-h', '--help', 'Show this message', STORE_NEW_VALUE, false]
}
}
}.each { |group, options| options.each { |name, definition| definition.last.freeze } }
end

begin
@@ -334,9 +334,9 @@ def construct_default_options

def self.default_option_values(group_options)
group_options.each_with_object({}) do |(option, data), group_option_defaults|
*args, block, default = data
*args, block, default = data
unless [TrueClass, FalseClass, NilClass, Fixnum].any? { |v| v === default }
default = default.clone
default = default.dup
end
group_option_defaults[option] = default
end
Original file line number Diff line number Diff line change
@@ -18,19 +18,19 @@ def foo
begin
loop do
x = foo
raise "value not correct" unless x == 200
raise 'value not correct' unless x == 200
Truffle::Primitive.assert_constant x
Truffle::Primitive.assert_not_compiled
end
rescue RubyTruffleError => e
if e.message.include? 'Truffle::Primitive.assert_not_compiled'
puts "attachments optimising"
puts 'attachments optimising'
exit 0
elsif e.message.include? 'Truffle::Primitive.assert_constant'
puts "attachments not optimising"
puts 'attachments not optimising'
exit 1
else
puts "some other error"
puts 'some other error'
exit 1
end
end
3 changes: 3 additions & 0 deletions test/truffle/compiler/stf-optimises.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

ruby -X+T -J-G:+TruffleCompilationExceptionsAreFatal test/truffle/compiler/stf-optimises/stf-optimises.rb
40 changes: 40 additions & 0 deletions test/truffle/compiler/stf-optimises/stf-optimises.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (c) 2015, 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

def foo
foo = 14
foo * 2
end

set_trace_func proc { |event, file, line, id, binding, classname|
if event == 'line' && file == __FILE__ && line == 11
binding.local_variable_set(:foo, 100)
end
}

begin
loop do
x = foo
raise 'value not correct' unless x == 200
Truffle::Primitive.assert_constant x
Truffle::Primitive.assert_not_compiled
end
rescue RubyTruffleError => e
if e.message.include? 'Truffle::Primitive.assert_not_compiled'
puts 'STF optimising'
exit 0
elsif e.message.include? 'Truffle::Primitive.assert_constant'
puts 'STF not optimising'
exit 1
else
puts 'some other error'
exit 1
end
end

exit 1
3 changes: 3 additions & 0 deletions test/truffle/compiler/tp-optimises.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

ruby -X+T -J-G:+TruffleCompilationExceptionsAreFatal test/truffle/compiler/tp-optimises/tp-optimises.rb
40 changes: 40 additions & 0 deletions test/truffle/compiler/tp-optimises/tp-optimises.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (c) 2015, 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

def foo
foo = 14
foo * 2
end

tp = TracePoint.new(:line) do |tp|
tp.binding.local_variable_set(:foo, 100)
end

tp.enable

begin
loop do
x = foo
raise 'value not correct' unless x == 200
Truffle::Primitive.assert_constant x
Truffle::Primitive.assert_not_compiled
end
rescue RubyTruffleError => e
if e.message.include? 'Truffle::Primitive.assert_not_compiled'
puts 'TP optimising'
exit 0
elsif e.message.include? 'Truffle::Primitive.assert_constant'
puts 'TP not optimising'
exit 1
else
puts 'some other error'
exit 1
end
end

exit 1
5 changes: 5 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/RubyContext.java
Original file line number Diff line number Diff line change
@@ -319,4 +319,9 @@ public CoreStrings getCoreStrings() {
public Object getClassVariableDefinitionLock() {
return classVariableDefinitionLock;
}

public Instrumenter getInstrumenter() {
return env.lookup(Instrumenter.class);
}

}
4 changes: 4 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/CoreLibrary.java
Original file line number Diff line number Diff line change
@@ -76,6 +76,7 @@
import org.jruby.truffle.core.thread.ThreadBacktraceLocationNodesFactory;
import org.jruby.truffle.core.thread.ThreadNodesFactory;
import org.jruby.truffle.core.time.TimeNodesFactory;
import org.jruby.truffle.core.tracepoint.TracePointNodesFactory;
import org.jruby.truffle.extra.TrufflePrimitiveNodesFactory;
import org.jruby.truffle.interop.TruffleInteropNodesFactory;
import org.jruby.truffle.language.RubyGuards;
@@ -464,6 +465,8 @@ public CoreLibrary(RubyContext context) {
weakRefClass = defineClass("WeakRef");
weakRefFactory = Layouts.WEAK_REF_LAYOUT.createWeakRefShape(weakRefClass, weakRefClass);
Layouts.CLASS.setInstanceFactoryUnsafe(weakRefClass, weakRefFactory);
final DynamicObject tracePointClass = defineClass("TracePoint");
Layouts.CLASS.setInstanceFactoryUnsafe(tracePointClass, Layouts.TRACE_POINT.createTracePointShape(tracePointClass, tracePointClass));

// Modules

@@ -634,6 +637,7 @@ public void addCoreMethods() {
coreMethodNodeManager.addCoreMethodNodes(PsychParserNodesFactory.getFactories());
coreMethodNodeManager.addCoreMethodNodes(PsychEmitterNodesFactory.getFactories());
coreMethodNodeManager.addCoreMethodNodes(AtomicReferenceNodesFactory.getFactories());
coreMethodNodeManager.addCoreMethodNodes(TracePointNodesFactory.getFactories());

coreMethodNodeManager.allMethodInstalled();

3 changes: 3 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/Layouts.java
Original file line number Diff line number Diff line change
@@ -78,6 +78,8 @@
import org.jruby.truffle.core.thread.ThreadLayoutImpl;
import org.jruby.truffle.core.time.TimeLayout;
import org.jruby.truffle.core.time.TimeLayoutImpl;
import org.jruby.truffle.core.tracepoint.TracePointLayout;
import org.jruby.truffle.core.tracepoint.TracePointLayoutImpl;
import org.jruby.truffle.stdlib.BigDecimalLayout;
import org.jruby.truffle.stdlib.BigDecimalLayoutImpl;
import org.jruby.truffle.stdlib.psych.EmitterLayout;
@@ -127,6 +129,7 @@ public abstract class Layouts {
public static final RandomizerLayout RANDOMIZER = RandomizerLayoutImpl.INSTANCE;
public static final AtomicReferenceLayout ATOMIC_REFERENCE = AtomicReferenceLayoutImpl.INSTANCE;
public static final HandleLayout HANDLE = HandleLayoutImpl.INSTANCE;
public static final TracePointLayout TRACE_POINT = TracePointLayoutImpl.INSTANCE;

// Other standard identifiers

145 changes: 74 additions & 71 deletions truffle/src/main/java/org/jruby/truffle/core/kernel/TraceManager.java
Original file line number Diff line number Diff line change
@@ -9,10 +9,10 @@
*/
package org.jruby.truffle.core.kernel;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.EventBinding;
import com.oracle.truffle.api.instrumentation.EventContext;
@@ -23,14 +23,12 @@
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.rope.CodeRange;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.arguments.RubyArguments;
import org.jruby.truffle.language.loader.SourceLoader;
import org.jruby.truffle.language.yield.YieldNode;

import java.util.ArrayList;
import java.util.Collection;
@@ -71,36 +69,38 @@ public void setTraceFunc(final DynamicObject traceFunc) {
instruments.add(instrumenter.attachFactory(SourceSectionFilter.newBuilder().tagIs(LINE_TAG).build(), new ExecutionEventNodeFactory() {
@Override
public ExecutionEventNode create(EventContext eventContext) {
final DynamicObject event = StringOperations.createString(context, StringOperations.encodeRope("line", UTF8Encoding.INSTANCE, CodeRange.CR_7BIT));
return new BaseEventEventNode(context, traceFunc, event);
return new BaseEventEventNode(context, traceFunc, context.getCoreStrings().LINE.createInstance());
}
}));

instruments.add(instrumenter.attachFactory(SourceSectionFilter.newBuilder().tagIs(CALL_TAG).build(), new ExecutionEventNodeFactory() {
@Override
public ExecutionEventNode create(EventContext eventContext) {
final DynamicObject event = StringOperations.createString(context, StringOperations.encodeRope("call", UTF8Encoding.INSTANCE, CodeRange.CR_7BIT));
return new CallEventEventNode(context, traceFunc, event);
return new CallEventEventNode(context, traceFunc, context.getCoreStrings().CALL.createInstance());
}
}));

instruments.add(instrumenter.attachFactory(SourceSectionFilter.newBuilder().tagIs(CLASS_TAG).build(), new ExecutionEventNodeFactory() {
@Override
public ExecutionEventNode create(EventContext eventContext) {
final DynamicObject event = StringOperations.createString(context, StringOperations.encodeRope("class", UTF8Encoding.INSTANCE, CodeRange.CR_7BIT));
return new BaseEventEventNode(context, traceFunc, event);
return new BaseEventEventNode(context, traceFunc, context.getCoreStrings().CLASS.createInstance());
}
}));

}

private final class BaseEventEventNode extends ExecutionEventNode {
private class BaseEventEventNode extends ExecutionEventNode {

private final ConditionProfile inTraceFuncProfile = ConditionProfile.createBinaryProfile();
protected final ConditionProfile inTraceFuncProfile = ConditionProfile.createBinaryProfile();

private final RubyContext context;
private final DynamicObject traceFunc;
private final Object event;
protected final RubyContext context;
protected final DynamicObject traceFunc;
protected final Object event;

@Child private YieldNode yieldNode;

@CompilationFinal private DynamicObject file;
@CompilationFinal private int line;

public BaseEventEventNode(RubyContext context, DynamicObject traceFunc, Object event) {
this.context = context;
@@ -110,96 +110,99 @@ public BaseEventEventNode(RubyContext context, DynamicObject traceFunc, Object e

@Override
protected void onEnter(VirtualFrame frame) {
if (!inTraceFuncProfile.profile(isInTraceFunc)) {
callSetTraceFunc(frame.materialize());
if (inTraceFuncProfile.profile(isInTraceFunc)) {
return;
}
}

@TruffleBoundary
private void callSetTraceFunc(MaterializedFrame frame) {
final SourceSection sourceSection = getEncapsulatingSourceSection();

final DynamicObject file = StringOperations.createString(context, StringOperations.encodeRope(sourceSection.getSource().getName(), UTF8Encoding.INSTANCE));
final int line = sourceSection.getStartLine();

final Object classname = context.getCoreLibrary().getNilObject();
final Object id = context.getCoreLibrary().getNilObject();

final DynamicObject binding = Layouts.BINDING.createBinding(context.getCoreLibrary().getBindingFactory(), frame);

isInTraceFunc = true;

try {
context.getCodeLoader().inline(this, frame, "traceFunc.call(event, file, line, id, binding, classname)", "traceFunc", traceFunc, "event", event, "file", file, "line", line, "id", id, "binding", binding, "classname", classname);
getYieldNode().dispatch(frame, traceFunc,
event,
getFile(),
getLine(),
context.getCoreLibrary().getNilObject(),
Layouts.BINDING.createBinding(context.getCoreLibrary().getBindingFactory(), frame.materialize()),
context.getCoreLibrary().getNilObject());
} finally {
isInTraceFunc = false;
isInTraceFunc = false;
}
}

}
private DynamicObject getFile() {
if (file == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
file = StringOperations.createString(context, context.getRopeTable().getRopeUTF8(getEncapsulatingSourceSection().getSource().getName()));
}

return file;
}

private final class CallEventEventNode extends ExecutionEventNode {
private int getLine() {
if (line == 0) {
CompilerDirectives.transferToInterpreterAndInvalidate();
line = getEncapsulatingSourceSection().getStartLine();
}

return line;
}

protected YieldNode getYieldNode() {
if (yieldNode == null) {
CompilerDirectives.transferToInterpreter();
yieldNode = insert(new YieldNode(context));
}

private final ConditionProfile inTraceFuncProfile = ConditionProfile.createBinaryProfile();
return yieldNode;
}

private final static String callTraceFuncCode = "traceFunc.call(event, file, line, id, binding, classname)";
}

private final RubyContext context;
private final DynamicObject traceFunc;
private final Object event;
private class CallEventEventNode extends BaseEventEventNode {

public CallEventEventNode(RubyContext context, DynamicObject traceFunc, Object event) {
this.context = context;
this.traceFunc = traceFunc;
this.event = event;
super(context, traceFunc, event);
}

@Override
protected void onEnter(VirtualFrame frame) {
if (!inTraceFuncProfile.profile(isInTraceFunc)) {
callSetTraceFunc(frame.materialize());
if (inTraceFuncProfile.profile(isInTraceFunc)) {
return;
}
}

@TruffleBoundary
private void callSetTraceFunc(MaterializedFrame frame) {
// set_trace_func reports the file and line of the call site.
final String filename;
final SourceSection sourceSection = getCallerSourceSection();

final String file;
final int line;
final SourceSection sourceSection = Truffle.getRuntime().getCallerFrame().getCallNode().getEncapsulatingSourceSection();

if (sourceSection.getSource() != null) {
// Skip over any lines that are a result of the trace function call being made.
if (sourceSection.getSource().getCode().equals(callTraceFuncCode)) {
return;
}

filename = sourceSection.getSource().getName();
file = sourceSection.getSource().getName();
line = sourceSection.getStartLine();
} else {
filename = "<internal>";
file = "<internal>";
line = -1;
}

final DynamicObject file = StringOperations.createString(context, StringOperations.encodeRope(filename, UTF8Encoding.INSTANCE));

if (!context.getOptions().INCLUDE_CORE_FILE_CALLERS_IN_SET_TRACE_FUNC && filename.startsWith(SourceLoader.TRUFFLE_SCHEME)) {
return;
}

final Object self = RubyArguments.getSelf(frame);
final Object classname = context.getCoreLibrary().getLogicalClass(self);
final Object id = context.getSymbolTable().getSymbol(RubyArguments.getMethod(frame).getName());

final DynamicObject binding = Layouts.BINDING.createBinding(context.getCoreLibrary().getBindingFactory(), Truffle.getRuntime().getCallerFrame().getFrame(FrameInstance.FrameAccess.MATERIALIZE, true).materialize());

isInTraceFunc = true;

try {
context.getCodeLoader().inline(this, frame, callTraceFuncCode, "traceFunc", traceFunc, "event", event, "file", file, "line", line, "id", id, "binding", binding, "classname", classname);
getYieldNode().dispatch(frame, traceFunc,
event,
file,
line,
context.getSymbolTable().getSymbol(RubyArguments.getMethod(frame).getName()),
Layouts.BINDING.createBinding(context.getCoreLibrary().getBindingFactory(), frame.materialize()),
context.getCoreLibrary().getLogicalClass(RubyArguments.getSelf(frame)));
} finally {
isInTraceFunc = false;
}
}

@TruffleBoundary
private SourceSection getCallerSourceSection() {
return Truffle.getRuntime().getCallerFrame().getCallNode().getEncapsulatingSourceSection();
}

}

}
11 changes: 11 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/rope/RopeTable.java
Original file line number Diff line number Diff line change
@@ -12,8 +12,11 @@

import com.oracle.truffle.api.CompilerDirectives;
import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;

import java.lang.ref.WeakReference;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.WeakHashMap;
import java.util.concurrent.locks.ReadWriteLock;
@@ -24,6 +27,14 @@ public class RopeTable {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final WeakHashMap<Key, WeakReference<Rope>> ropesTable = new WeakHashMap<>();

@CompilerDirectives.TruffleBoundary
public Rope getRopeUTF8(String string) {
return getRope(
string.getBytes(StandardCharsets.UTF_8),
UTF8Encoding.INSTANCE,
CodeRange.CR_VALID);
}

@CompilerDirectives.TruffleBoundary
public Rope getRope(byte[] bytes, Encoding encoding, CodeRange codeRange) {
final Key key = new Key(bytes, encoding);
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ public class CoreString {
private final String literal;

@CompilationFinal private volatile Rope rope;
@CompilationFinal private volatile DynamicObject symbol;

public CoreString(RubyContext context, String literal) {
assert is7Bit(literal);
@@ -35,17 +36,23 @@ public CoreString(RubyContext context, String literal) {
public Rope getRope() {
if (rope == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
rope = createRope();

rope = context.getRopeTable().getRope(
literal.getBytes(StandardCharsets.US_ASCII),
ASCIIEncoding.INSTANCE,
CodeRange.CR_7BIT);
}

return rope;
}

private Rope createRope() {
// RopeTable.getRope is fully synchronized and returns the same object for equivalent parameters
return context.getRopeTable().getRope(
literal.getBytes(StandardCharsets.US_ASCII),
ASCIIEncoding.INSTANCE,
CodeRange.CR_7BIT);
public DynamicObject getSymbol() {
if (symbol == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
symbol = context.getSymbolTable().getSymbol(getRope());
}

return symbol;
}

public DynamicObject createInstance() {
Original file line number Diff line number Diff line change
@@ -14,11 +14,14 @@
public class CoreStrings {

public final CoreString ASSIGNMENT;
public final CoreString CALL;
public final CoreString CLASS;
public final CoreString CLASS_VARIABLE;
public final CoreString EXPRESSION;
public final CoreString FALSE;
public final CoreString GLOBAL_VARIABLE;
public final CoreString INSTANCE_VARIABLE;
public final CoreString LINE;
public final CoreString LOCAL_VARIABLE;
public final CoreString NIL;
public final CoreString SELF;
@@ -27,11 +30,14 @@ public class CoreStrings {

public CoreStrings(RubyContext context) {
ASSIGNMENT = new CoreString(context, "assignment");
CALL = new CoreString(context, "call");
CLASS = new CoreString(context, "class");
CLASS_VARIABLE = new CoreString(context, "class variable");
EXPRESSION = new CoreString(context, "expression");
FALSE = new CoreString(context, "false");
GLOBAL_VARIABLE = new CoreString(context, "global-variable");
INSTANCE_VARIABLE = new CoreString(context, "instance-variable");
LINE = new CoreString(context, "line");
LOCAL_VARIABLE = new CoreString(context, "local-variable");
NIL = new CoreString(context, "nil");
SELF = new CoreString(context, "self");
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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.core.tracepoint;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.ExecutionEventNode;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.yield.YieldNode;

class TracePointEventNode extends ExecutionEventNode {

private final ConditionProfile inTraceFuncProfile = ConditionProfile.createBinaryProfile();

private final RubyContext context;
private final DynamicObject tracePoint;

@Child private YieldNode yieldNode;

@CompilationFinal private DynamicObject path;
@CompilationFinal private int line;

public TracePointEventNode(RubyContext context, DynamicObject tracePoint) {
this.context = context;
this.tracePoint = tracePoint;
}

@Override
protected void onEnter(VirtualFrame frame) {
if (inTraceFuncProfile.profile(Layouts.TRACE_POINT.getInsideProc(tracePoint))) {
return;
}

Layouts.TRACE_POINT.setEvent(tracePoint, context.getCoreStrings().LINE.getSymbol());
Layouts.TRACE_POINT.setPath(tracePoint, getPath());
Layouts.TRACE_POINT.setLine(tracePoint, getLine());
Layouts.TRACE_POINT.setBinding(tracePoint, Layouts.BINDING.createBinding(context.getCoreLibrary().getBindingFactory(), frame.materialize()));

Layouts.TRACE_POINT.setInsideProc(tracePoint, true);

try {
getYieldNode().dispatch(frame, Layouts.TRACE_POINT.getProc(tracePoint), tracePoint);
} finally {
Layouts.TRACE_POINT.setInsideProc(tracePoint, false);
}
}

private DynamicObject getPath() {
if (path == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
path = StringOperations.createString(context, context.getRopeTable().getRopeUTF8(getEncapsulatingSourceSection().getSource().getName()));
}

return path;
}

private int getLine() {
if (line == 0) {
CompilerDirectives.transferToInterpreterAndInvalidate();
line = getEncapsulatingSourceSection().getStartLine();
}

return line;
}

protected YieldNode getYieldNode() {
if (yieldNode == null) {
CompilerDirectives.transferToInterpreter();
yieldNode = insert(new YieldNode(context));
}

return yieldNode;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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.core.tracepoint;

import com.oracle.truffle.api.instrumentation.EventBinding;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.DynamicObjectFactory;
import com.oracle.truffle.api.object.dsl.Layout;
import com.oracle.truffle.api.object.dsl.Nullable;
import org.jruby.truffle.core.basicobject.BasicObjectLayout;

@Layout
public interface TracePointLayout extends BasicObjectLayout {

DynamicObjectFactory createTracePointShape(DynamicObject logicalClass,
DynamicObject metaClass);

DynamicObject createTracePoint(
DynamicObjectFactory factory,
@Nullable String[] tags,
@Nullable DynamicObject event,
@Nullable DynamicObject path,
int line,
@Nullable DynamicObject binding,
@Nullable DynamicObject proc,
@Nullable EventBinding eventBinding,
boolean insideProc);

boolean isTracePoint(DynamicObject object);

String[] getTags(DynamicObject object);
void setTags(DynamicObject object, String[] value);

DynamicObject getEvent(DynamicObject object);
void setEvent(DynamicObject object, DynamicObject value);

DynamicObject getPath(DynamicObject object);
void setPath(DynamicObject object, DynamicObject value);

int getLine(DynamicObject object);
void setLine(DynamicObject object, int value);

DynamicObject getBinding(DynamicObject object);
void setBinding(DynamicObject object, DynamicObject value);

DynamicObject getProc(DynamicObject object);
void setProc(DynamicObject object, DynamicObject value);

EventBinding getEventBinding(DynamicObject object);
void setEventBinding(DynamicObject object, EventBinding value);

boolean getInsideProc(DynamicObject object);
void setInsideProc(DynamicObject object, boolean value);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* 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.core.tracepoint;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.EventBinding;
import com.oracle.truffle.api.instrumentation.EventContext;
import com.oracle.truffle.api.instrumentation.ExecutionEventNode;
import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.CoreClass;
import org.jruby.truffle.core.CoreMethod;
import org.jruby.truffle.core.CoreMethodArrayArgumentsNode;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.UnaryCoreMethodNode;
import org.jruby.truffle.core.YieldingCoreMethodNode;
import org.jruby.truffle.core.kernel.TraceManager;
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.objects.AllocateObjectNode;
import org.jruby.truffle.language.objects.AllocateObjectNodeGen;

@CoreClass(name = "TracePoint")
public abstract class TracePointNodes {

@CoreMethod(names = "allocate", constructor = true)
public abstract static class AllocateNode extends UnaryCoreMethodNode {

@Child private AllocateObjectNode allocateNode;

public AllocateNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
allocateNode = AllocateObjectNodeGen.create(context, sourceSection, null, null);
}

@Specialization
public DynamicObject allocate(DynamicObject rubyClass) {
return allocateNode.allocate(rubyClass, null, null, null, 0, null, null, null, false);
}

}

@CoreMethod(names = "initialize", rest = true, needsBlock = true)
public abstract static class InitializeNode extends CoreMethodArrayArgumentsNode {

public InitializeNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isTracePoint(tracePoint)")
public DynamicObject initialize(DynamicObject tracePoint, Object[] args, DynamicObject block) {
Layouts.TRACE_POINT.setTags(tracePoint, new String[]{TraceManager.LINE_TAG});
Layouts.TRACE_POINT.setProc(tracePoint, block);
return tracePoint;
}

}

@CoreMethod(names = "enable", needsBlock = true)
public abstract static class EnableNode extends YieldingCoreMethodNode {

public EnableNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isTracePoint(tracePoint)")
public boolean enable(VirtualFrame frame, DynamicObject tracePoint, NotProvided block) {
return enable(frame, tracePoint, (DynamicObject) null);
}

@Specialization(guards = "isTracePoint(tracePoint)")
public boolean enable(VirtualFrame frame, final DynamicObject tracePoint, DynamicObject block) {
CompilerDirectives.bailout("TracePoint#enable can't be compiled");

EventBinding<?> eventBinding = Layouts.TRACE_POINT.getEventBinding(tracePoint);
final boolean alreadyEnabled = eventBinding != null;

if (!alreadyEnabled) {
eventBinding = createEventBinding(getContext(), tracePoint);
Layouts.TRACE_POINT.setEventBinding(tracePoint, eventBinding);
}

if (block != null) {
try {
yield(frame, block);
} finally {
if (!alreadyEnabled) {
eventBinding.dispose();
Layouts.TRACE_POINT.setEventBinding(tracePoint, null);
}
}
}

return alreadyEnabled;
}

public static EventBinding<?> createEventBinding(final RubyContext context, final DynamicObject tracePoint) {
return context.getInstrumenter().attachFactory(SourceSectionFilter.newBuilder().tagIs(Layouts.TRACE_POINT.getTags(tracePoint)).build(), new ExecutionEventNodeFactory() {
@Override
public ExecutionEventNode create(EventContext eventContext) {
return new TracePointEventNode(context, tracePoint);
}
});
}

}

@CoreMethod(names = "disable", needsBlock = true)
public abstract static class DisableNode extends YieldingCoreMethodNode {

public DisableNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isTracePoint(tracePoint)")
public boolean disable(VirtualFrame frame, DynamicObject tracePoint, NotProvided block) {
return disable(frame, tracePoint, (DynamicObject) null);
}

@Specialization(guards = "isTracePoint(tracePoint)")
public boolean disable(VirtualFrame frame, final DynamicObject tracePoint, DynamicObject block) {
CompilerDirectives.bailout("TracePoint#disable can't be compiled");

EventBinding<?> eventBinding = Layouts.TRACE_POINT.getEventBinding(tracePoint);
final boolean alreadyEnabled = eventBinding != null;

if (alreadyEnabled) {
eventBinding.dispose();
Layouts.TRACE_POINT.setEventBinding(tracePoint, null);
}

if (block != null) {
try {
yield(frame, block);
} finally {
if (alreadyEnabled) {
eventBinding = EnableNode.createEventBinding(getContext(), tracePoint);
Layouts.TRACE_POINT.setEventBinding(tracePoint, eventBinding);
}
}
}

return alreadyEnabled;
}

}

@CoreMethod(names = "enabled?")
public abstract static class EnabledNode extends CoreMethodArrayArgumentsNode {

public EnabledNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isTracePoint(tracePoint)")
public boolean enabled(DynamicObject tracePoint) {
return Layouts.TRACE_POINT.getEventBinding(tracePoint) != null;
}

}

@CoreMethod(names = "event")
public abstract static class EventNode extends CoreMethodArrayArgumentsNode {

public EventNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isTracePoint(tracePoint)")
public DynamicObject event(DynamicObject tracePoint) {
return Layouts.TRACE_POINT.getEvent(tracePoint);
}

}

@CoreMethod(names = "path")
public abstract static class PathNode extends CoreMethodArrayArgumentsNode {

public PathNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isTracePoint(tracePoint)")
public DynamicObject path(DynamicObject tracePoint) {
return Layouts.TRACE_POINT.getPath(tracePoint);
}

}

@CoreMethod(names = "lineno")
public abstract static class LineNode extends CoreMethodArrayArgumentsNode {

public LineNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isTracePoint(tracePoint)")
public int line(DynamicObject tracePoint) {
return Layouts.TRACE_POINT.getLine(tracePoint);
}

}

@CoreMethod(names = "binding")
public abstract static class BindingNode extends CoreMethodArrayArgumentsNode {

public BindingNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isTracePoint(tracePoint)")
public DynamicObject binding(DynamicObject tracePoint) {
return Layouts.TRACE_POINT.getBinding(tracePoint);
}

}

}
2 changes: 0 additions & 2 deletions truffle/src/main/java/org/jruby/truffle/language/Options.java
Original file line number Diff line number Diff line change
@@ -32,7 +32,6 @@
import static org.jruby.util.cli.Options.TRUFFLE_EXCEPTIONS_PRINT_UNCAUGHT_JAVA;
import static org.jruby.util.cli.Options.TRUFFLE_EXCEPTIONS_STORE_JAVA;
import static org.jruby.util.cli.Options.TRUFFLE_HASH_PACKED_ARRAY_MAX;
import static org.jruby.util.cli.Options.TRUFFLE_INCLUDE_CORE_FILE_CALLERS_IN_SET_TRACE_FUNC;
import static org.jruby.util.cli.Options.TRUFFLE_INLINE_NEEDS_CALLER_FRAME;
import static org.jruby.util.cli.Options.TRUFFLE_INSTANCE_VARIABLE_CACHE;
import static org.jruby.util.cli.Options.TRUFFLE_INSTRUMENTATION_SERVER_PORT;
@@ -114,7 +113,6 @@ public class Options {
public final boolean BACKTRACES_INTERLEAVE_JAVA = TRUFFLE_BACKTRACES_INTERLEAVE_JAVA.load();
public final int BACKTRACES_LIMIT = TRUFFLE_BACKTRACES_LIMIT.load();
public final boolean BACKTRACES_OMIT_UNUSED = TRUFFLE_BACKTRACES_OMIT_UNUSED.load();
public final boolean INCLUDE_CORE_FILE_CALLERS_IN_SET_TRACE_FUNC = TRUFFLE_INCLUDE_CORE_FILE_CALLERS_IN_SET_TRACE_FUNC.load();

// Call graph

Original file line number Diff line number Diff line change
@@ -220,6 +220,10 @@ public static boolean isHandle(DynamicObject object) {
return Layouts.HANDLE.isHandle(object);
}

public static boolean isTracePoint(DynamicObject object) {
return Layouts.TRACE_POINT.isTracePoint(object);
}

// Internal types

public static boolean isThreadLocal(Object value) {
Original file line number Diff line number Diff line change
@@ -95,19 +95,9 @@ public List<String> formatBacktrace(RubyContext context, DynamicObject exception
final List<Activation> activations = backtrace.getActivations();
final ArrayList<String> lines = new ArrayList<>();

try {
lines.add(formatInLine(activations, exception));
} catch (Exception e) {
if (context.getOptions().EXCEPTIONS_PRINT_JAVA) {
e.printStackTrace();
}

lines.add(String.format("(exception %s %s", e.getMessage(), e.getStackTrace()[0].toString()));
}

for (int n = 1; n < activations.size(); n++) {
for (int n = 0; n < activations.size(); n++) {
try {
lines.add(formatFromLine(activations, n));
lines.add(formatLine(activations, n, exception));
} catch (Exception e) {
if (context.getOptions().EXCEPTIONS_PRINT_JAVA) {
e.printStackTrace();
@@ -124,84 +114,7 @@ public List<String> formatBacktrace(RubyContext context, DynamicObject exception
return lines;
}

private String formatInLine(List<Activation> activations, DynamicObject exception) {
final StringBuilder builder = new StringBuilder();

if (activations.isEmpty()) {
throw new UnsupportedOperationException("At least one activation is required.");
}

final Activation activation = activations.get(0);

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

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

if (activation.getCallNode().getRootNode() instanceof RubyRootNode) {
final SourceSection sourceSection = activation.getCallNode().getEncapsulatingSourceSection();
final SourceSection reportedSourceSection;
final String reportedName;

if (isCore(sourceSection) && !flags.contains(FormattingFlags.INCLUDE_CORE_FILES)) {
reportedSourceSection = nextUserSourceSection(activations, 1);
reportedName = activation.getMethod().getName();
} else {
reportedSourceSection = sourceSection;
reportedName = reportedSourceSection.getIdentifier();
}

if (reportedSourceSection == null || reportedSourceSection.getSource() == null) {
builder.append("???");
} else {
builder.append(reportedSourceSection.getSource().getName());
builder.append(":");
builder.append(reportedSourceSection.getStartLine());
builder.append(":in `");
builder.append(reportedName);
builder.append("'");
}
} else {
builder.append(formatForeign(activation.getCallNode()));
}

if (!flags.contains(FormattingFlags.OMIT_EXCEPTION) && exception != null) {
String message;
try {
Object messageObject = context.send(exception, "message", null);
if (RubyGuards.isRubyString(messageObject)) {
message = messageObject.toString();
} else {
message = Layouts.EXCEPTION.getMessage(exception).toString();
}
} catch (RaiseException e) {
message = Layouts.EXCEPTION.getMessage(exception).toString();
}

builder.append(": ");
builder.append(message);
builder.append(" (");
builder.append(Layouts.MODULE.getFields(Layouts.BASIC_OBJECT.getLogicalClass(exception)).getName());
builder.append(")");
}

return builder.toString();
}

private String formatFromLine(List<Activation> activations, int n) {
final String formattedLine = formatLine(activations, n);

if (flags.contains(FormattingFlags.OMIT_FROM_PREFIX)) {
return formattedLine;
} else {
return "\tfrom " + formattedLine;
}
}

public String formatLine(List<Activation> activations, int n) {
public String formatLine(List<Activation> activations, int n, DynamicObject exception) {
final Activation activation = activations.get(n);

if (activation == Activation.OMITTED_LIMIT) {
@@ -214,6 +127,10 @@ public String formatLine(List<Activation> activations, int n) {

final StringBuilder builder = new StringBuilder();

if (!flags.contains(FormattingFlags.OMIT_FROM_PREFIX) && n > 0) {
builder.append("\tfrom ");
}

if (activation.getCallNode().getRootNode() instanceof RubyRootNode) {
final SourceSection sourceSection = activation.getCallNode().getEncapsulatingSourceSection();
final SourceSection reportedSourceSection;
@@ -249,6 +166,26 @@ public String formatLine(List<Activation> activations, int n) {
builder.append(formatForeign(activation.getCallNode()));
}

if (!flags.contains(FormattingFlags.OMIT_EXCEPTION) && exception != null && n == 0) {
String message;
try {
Object messageObject = context.send(exception, "message", null);
if (RubyGuards.isRubyString(messageObject)) {
message = messageObject.toString();
} else {
message = Layouts.EXCEPTION.getMessage(exception).toString();
}
} catch (RaiseException e) {
message = Layouts.EXCEPTION.getMessage(exception).toString();
}

builder.append(": ");
builder.append(message);
builder.append(" (");
builder.append(Layouts.MODULE.getFields(Layouts.BASIC_OBJECT.getLogicalClass(exception)).getName());
builder.append(")");
}

return builder.toString();
}

Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@ public void run(MaterializedFrame frame, Node currentNode) {
}

shellInterface.getWriter().println(
formatter.formatLine(Collections.singletonList(activation), 0));
formatter.formatLine(Collections.singletonList(activation), 0, null));

n++;
}