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: 9dfb4fb59ef6
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 8899ea8ab9a9
Choose a head ref
  • 2 commits
  • 8 files changed
  • 1 contributor

Commits on Mar 4, 2015

  1. Fix up backtrace generation.

    * Get JIT emitting encoded method names.
    * Get backtrace generation decoding JITed names.
    * Add module, class, metaclass trace elements.
    * Align some naming with MRI for metaclass, script body, etc.
    headius committed Mar 4, 2015
    Copy the full SHA
    c81d9e1 View commit details
  2. Copy the full SHA
    8899ea8 View commit details
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ env:
matrix:
- PHASE='-Ptest'
- PHASE='-Prake -Dtask=test:jruby'
- PHASE='-Prake -Dtask=test:jruby:jit'
- PHASE='-Prake -Dtask=test:mri'
- PHASE='-Prake -Dtask=test:mri:jit'
- PHASE='-Prake -Dtask=test:slow_suites'
Original file line number Diff line number Diff line change
@@ -35,19 +35,49 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

return callInternal(context, self, clazz, name, block);
}

protected IRubyObject callInternal(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
InterpreterContext ic = ensureInstrsReady();
if (ic.hasExplicitCallProtocol()) {
return ic.engine.interpret(context, self, ic, getImplementationClass().getMethodLocation(), name, block, null);
} else {
try {
this.pre(ic, context, self, name, block, getImplementationClass());
return ic.engine.interpret(context, self, ic, getImplementationClass().getMethodLocation(), name, block, null);
} finally {
if (!ic.hasExplicitCallProtocol()) {
this.pre(ic, context, self, name, block, getImplementationClass());
}
try {
switch (method.getScopeType()) {
case MODULE_BODY: return INTERPRET_MODULE(ic, context, self, clazz, method.getName(), block);
case CLASS_BODY: return INTERPRET_CLASS(ic, context, self, clazz, method.getName(), block);
case METACLASS_BODY: return INTERPRET_METACLASS(ic, context, self, clazz, "singleton class", block);
default: throw new RuntimeException("invalid body method type: " + method);
}
} finally {
if (!ic.hasExplicitCallProtocol()) {
this.post(ic, context);
}
}
}

private IRubyObject INTERPRET_METACLASS(InterpreterContext ic, ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
return interpretWithBacktrace(ic, context, self, name, block);
}

private IRubyObject INTERPRET_MODULE(InterpreterContext ic, ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
return interpretWithBacktrace(ic, context, self, name, block);
}

private IRubyObject INTERPRET_CLASS(InterpreterContext ic, ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
return interpretWithBacktrace(ic, context, self, name, block);
}

private IRubyObject interpretWithBacktrace(InterpreterContext ic, ThreadContext context, IRubyObject self, String name, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());
return ic.engine.interpret(context, self, ic, getImplementationClass().getMethodLocation(), name, block, null);
} finally {
ThreadContext.popBacktrace(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
return call(context, self, clazz, name, block);
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ public InterpretedIRMetaClassBody(IRScope metaClassBody, RubyModule implementati
super(metaClassBody, implementationClass);
}

@Override
protected void post(InterpreterContext ic, ThreadContext context) {
// update call stacks (pop: ..)
context.popFrame();
@@ -22,7 +23,8 @@ protected void post(InterpreterContext ic, ThreadContext context) {
}
}

protected void pre(InterpreterContext ic, ThreadContext context, IRubyObject self, String name, Block block) {
@Override
protected void pre(InterpreterContext ic, ThreadContext context, IRubyObject self, String name, Block block, RubyModule implClass) {
// update call stacks (push: frame, class, scope, etc.)
context.preMethodFrameOnly(getImplementationClass(), name, self, block);
if (ic.pushNewDynScope()) {
@@ -41,21 +43,9 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
DynamicMethod actualMethod = box.actualMethod;
if (actualMethod != null) return actualMethod.call(context, self, clazz, name, block);

InterpreterContext ic = ensureInstrsReady();

if (IRRuntimeHelpers.isDebug()) doDebug();

if (ic.hasExplicitCallProtocol()) {
return ic.engine.interpret(context, self, ic, getImplementationClass().getMethodLocation(), name, block, null);
} else {
try {
pre(ic, context, self, name, block);

return ic.engine.interpret(context, self, ic, getImplementationClass().getMethodLocation(), name, block, null);
} finally {
post(ic, context);
}
}
return callInternal(context, self, clazz, name, block);
}

@Override
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/Compiler.java
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ protected ScriptAndCode execute(final Ruby runtime, final IRScriptBody scope, Cl
bytecode = visitor.compileToBytecode(scope);
compiled = visitor.defineFromBytecode(scope, bytecode, classLoader);

Method compiledMethod = compiled.getMethod("__script__", ThreadContext.class,
Method compiledMethod = compiled.getMethod("RUBY$script", ThreadContext.class,
StaticScope.class, IRubyObject.class, IRubyObject[].class, Block.class, RubyModule.class, String.class);
_compiledHandle = MethodHandles.publicLookup().unreflect(compiledMethod);
} catch (NotCompilableException nce) {
30 changes: 10 additions & 20 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -235,26 +235,29 @@ public static final Signature signatureFor(IRScope method, boolean aritySplit) {
.appendArgs(new String[]{"context", "scope", "self", "args", "block", "superName", "type"}, ThreadContext.class, StaticScope.class, IRubyObject.class, IRubyObject[].class, Block.class, String.class, Block.Type.class);

public void emitScriptBody(IRScriptBody script) {
// Note: no index attached because there should be at most one script body per .class
String name = JavaNameMangler.encodeScopeForBacktrace(script);
String clsName = jvm.scriptToClass(script.getFileName());
jvm.pushscript(clsName, script.getFileName());

emitScope(script, "__script__", signatureFor(script, false), false);
emitScope(script, name, signatureFor(script, false), false);

jvm.cls().visitEnd();
jvm.popclass();
}

public void emitMethod(IRMethod method) {
String name = JavaNameMangler.mangleMethodName(method.getName() + "_" + methodIndex++);
String name = JavaNameMangler.encodeScopeForBacktrace(method) + "$" + methodIndex++;

emitWithSignatures(method, name);
}

public void emitMethodJIT(IRMethod method) {
String clsName = jvm.scriptToClass(method.getFileName());
String name = JavaNameMangler.encodeScopeForBacktrace(method) + "$" + methodIndex++;
jvm.pushscript(clsName, method.getFileName());

emitWithSignatures(method, "__script__");
emitWithSignatures(method, name);

jvm.cls().visitEnd();
jvm.popclass();
@@ -275,19 +278,13 @@ private void emitWithSignatures(IRMethod method, String name) {
}

public Handle emitModuleBodyJIT(IRModuleBody method) {
String baseName = method.getName() + "_" + methodIndex++;
String name;
String name = JavaNameMangler.encodeScopeForBacktrace(method) + "$" + methodIndex++;

if (baseName.indexOf("DUMMY_MC") != -1) {
name = "METACLASS_" + methodIndex++;
} else {
name = baseName + "_" + methodIndex++;
}
String clsName = jvm.scriptToClass(method.getFileName());
jvm.pushscript(clsName, method.getFileName());

Signature signature = signatureFor(method, false);
emitScope(method, "__script__", signature, false);
emitScope(method, name, signature, false);

Handle handle = new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(signature.type().returnType(), signature.type().parameterArray()));

@@ -306,22 +303,15 @@ private void emitClosures(IRScope s) {

public Handle emitClosure(IRClosure closure) {
/* Compile the closure like a method */
String name = JavaNameMangler.mangleMethodName(closure.getName() + "__" + closure.getLexicalParent().getName() + "_" + methodIndex++);
String name = JavaNameMangler.encodeScopeForBacktrace(closure) + "$" + methodIndex++;

emitScope(closure, name, CLOSURE_SIGNATURE, false);

return new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(CLOSURE_SIGNATURE.type().returnType(), CLOSURE_SIGNATURE.type().parameterArray()));
}

public Handle emitModuleBody(IRModuleBody method) {
String baseName = method.getName() + "_" + methodIndex++;
String name;

if (baseName.indexOf("DUMMY_MC") != -1) {
name = "METACLASS_" + methodIndex++;
} else {
name = baseName + "_" + methodIndex++;
}
String name = JavaNameMangler.encodeScopeForBacktrace(method) + "$" + methodIndex++;

Signature signature = signatureFor(method, false);
emitScope(method, name, signature, false);
78 changes: 23 additions & 55 deletions core/src/main/java/org/jruby/runtime/backtrace/BacktraceData.java
Original file line number Diff line number Diff line change
@@ -71,63 +71,19 @@ private RubyStackTraceElement[] transformBacktrace(Map<String, Map<String, Strin
// Don't process .java files
if (!filename.endsWith(".java")) {

boolean compiled = false;
int index = -1;

// Check for compiled name markers
// FIXME: Formalize jitted method structure so this isn't quite as hacky
if (className.startsWith(JITCompiler.RUBY_JIT_PREFIX)) {

// JIT-compiled code
compiled = true;

// pull out and demangle the method name
String tmpClassName = className;
int start = JITCompiler.RUBY_JIT_PREFIX.length() + 1;
int hash = tmpClassName.indexOf(JITCompiler.CLASS_METHOD_DELIMITER, start);
int end = tmpClassName.lastIndexOf("_");
if( hash != -1 ) { // TODO in case the class file was loaded by jit codeCache. Is this right
className = tmpClassName.substring(start, hash);
}
methodName = tmpClassName.substring(hash + JITCompiler.CLASS_METHOD_DELIMITER.length(), end);

} else if ((index = methodName.indexOf("$RUBY$")) >= 0) {
String decodedName = JavaNameMangler.decodeMethodForBacktrace(methodName);

// AOT-compiled code
compiled = true;
if (decodedName != null) {
// construct Ruby trace element
RubyStackTraceElement rubyElement = new RubyStackTraceElement(className, decodedName, filename, line, false);

// pull out and demangle the method name
index += "$RUBY$".length();
if (methodName.indexOf("SYNTHETIC", index) == index) {
methodName = methodName.substring(index + "SYNTHETIC".length());
} else {
methodName = methodName.substring(index);
}

}

// demangle any JVM-prohibited names
methodName = JavaNameMangler.demangleMethodName(methodName);

// root body gets named (root)
if (methodName.equals("__file__")) methodName = "(root)";

// construct Ruby trace element
RubyStackTraceElement rubyElement = new RubyStackTraceElement(className, methodName, filename, line, false);

// add duplicate if masking native and previous frame was native (Kernel#caller)
if (maskNative && dupFrame) {
dupFrame = false;
trace.add(new RubyStackTraceElement(className, dupFrameName, filename, line, false));
}
trace.add(rubyElement);

if (compiled) {
// if it's a synthetic call, gobble up parent calls
// TODO: need to formalize this better
while (element.getMethodName().contains("$RUBY$SYNTHETIC") && ++i < javaTrace.length) {
element = javaTrace[i];
// add duplicate if masking native and previous frame was native (Kernel#caller)
if (maskNative && dupFrame) {
dupFrame = false;
trace.add(new RubyStackTraceElement(className, dupFrameName, filename, line, false));
}
trace.add(rubyElement);
continue;
}
}
}
@@ -169,8 +125,20 @@ private RubyStackTraceElement[] transformBacktrace(Map<String, Map<String, Strin
// pop interpreter frame
BacktraceElement rubyFrame = rubyTrace[rubyFrameIndex--];

FrameType frameType = FrameType.INTERPRETED_FRAMES.get(methodName);

// construct Ruby trace element
RubyStackTraceElement rubyElement = new RubyStackTraceElement("RUBY", rubyFrame.method, rubyFrame.filename, rubyFrame.line + 1, false);
String newName = rubyFrame.method;
switch (frameType) {
case METHOD: newName = rubyFrame.method; break;
case BLOCK: newName = "block in " + rubyFrame.method; break;
case CLASS: newName = "<class:" + rubyFrame.method + ">"; break;
case MODULE: newName = "<module:" + rubyFrame.method + ">"; break;
case METACLASS: newName = "singleton class"; break;
case ROOT: newName = "<top>"; break;
case EVAL: newName = "<eval>"; break;
}
RubyStackTraceElement rubyElement = new RubyStackTraceElement("RUBY", newName, rubyFrame.filename, rubyFrame.line + 1, false);

// dup if masking native and previous frame was native
if (maskNative && dupFrame) {
Original file line number Diff line number Diff line change
@@ -5,21 +5,27 @@
import java.util.Map;
import java.util.Set;

import org.jruby.internal.runtime.methods.InterpretedIRBodyMethod;
import org.jruby.internal.runtime.methods.InterpretedIRMetaClassBody;
import org.jruby.internal.runtime.methods.InterpretedIRMethod;
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.runtime.InterpretedIRBlockBody;

public enum FrameType {
METHOD, BLOCK, EVAL, CLASS, ROOT;
METHOD, BLOCK, EVAL, CLASS, MODULE, METACLASS, ROOT;
public static final Set<String> INTERPRETED_CLASSES = new HashSet<String>();
public static final Map<String, FrameType> INTERPRETED_FRAMES = new HashMap<String, FrameType>();

static {
INTERPRETED_CLASSES.add(Interpreter.class.getName());
INTERPRETED_CLASSES.add(InterpretedIRMethod.class.getName());
INTERPRETED_CLASSES.add(InterpretedIRBodyMethod.class.getName());

INTERPRETED_FRAMES.put("INTERPRET_METHOD", FrameType.METHOD);
INTERPRETED_FRAMES.put("INTERPRET_EVAL", FrameType.EVAL);
INTERPRETED_FRAMES.put("INTERPRET_CLASS", FrameType.CLASS);
INTERPRETED_FRAMES.put("INTERPRET_MODULE", FrameType.MODULE);
INTERPRETED_FRAMES.put("INTERPRET_METACLASS", FrameType.METACLASS);
INTERPRETED_FRAMES.put("INTERPRET_BLOCK", FrameType.BLOCK);
INTERPRETED_FRAMES.put("INTERPRET_ROOT", FrameType.ROOT);
}
51 changes: 51 additions & 0 deletions core/src/main/java/org/jruby/util/JavaNameMangler.java
Original file line number Diff line number Diff line change
@@ -5,6 +5,13 @@

package org.jruby.util;

import org.jruby.ir.IRClassBody;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRMetaClassBody;
import org.jruby.ir.IRMethod;
import org.jruby.ir.IRModuleBody;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRScriptBody;
import org.jruby.platform.Platform;

import java.io.FileNotFoundException;
@@ -249,4 +256,48 @@ private static int escapeChar(char character) {
private static char unescapeChar(char character) {
return DANGEROUS_CHARS.charAt(REPLACEMENT_CHARS.indexOf(character));
}

public static String encodeScopeForBacktrace(IRScope scope) {
if (scope instanceof IRMethod) {
return "RUBY$method$" + mangleMethodName(scope.getName());
} else if (scope instanceof IRClosure) {
return "RUBY$block$" + mangleMethodName(scope.getNearestTopLocalVariableScope().getName());
} else if (scope instanceof IRMetaClassBody) {
return "RUBY$metaclass";
} else if (scope instanceof IRClassBody) {
return "RUBY$class$" + mangleMethodName(scope.getName());
} else if (scope instanceof IRModuleBody) {
return "RUBY$module$" + mangleMethodName(scope.getName());
} else if (scope instanceof IRScriptBody) {
return "RUBY$script";
}
throw new RuntimeException("unknown scope type for backtrace encoding: " + scope.getClass());
}

public static String decodeMethodForBacktrace(String methodName) {
if (!methodName.startsWith("RUBY$")) return null;

String[] elts = methodName.split("\\$");
String type = elts[1];
String name;

// root body gets named (root)
switch (type) {
case "script":
return "<top>";
case "metaclass":
return "singleton class";
}

// remaining cases have an encoded name
name = demangleMethodName(elts[2]);
switch (type) {
case "method": return name;
case "block": return "block in " + name;
case "class": // fall through
case "module": return "<" + type + ":" + name + ">";
default:
throw new RuntimeException("unknown encoded method type '" + type + "' from '" + methodName);
}
}
}