Skip to content

Commit

Permalink
Showing 5 changed files with 108 additions and 1 deletion.
3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -292,9 +292,10 @@ 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_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.");
public static final Option<Boolean> TRUFFLE_CALL_FREQUENCY = bool(TRUFFLE, "truffle.call_frequency", false, "Print call frequencies every second.");

public static final Option<Boolean> TRUFFLE_CALL_GRAPH = bool(TRUFFLE, "truffle.callgraph", false, "Maintain a call graph.");
public static final Option<String> TRUFFLE_CALL_GRAPH_WRITE = string(TRUFFLE, "truffle.callgraph.write", "File to write the call garph to on exit.");

14 changes: 14 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/RubyContext.java
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@
import org.jruby.truffle.platform.NativePlatform;
import org.jruby.truffle.platform.NativePlatformFactory;
import org.jruby.truffle.stdlib.CoverageManager;
import org.jruby.truffle.tools.CallFrequency;
import org.jruby.truffle.tools.InstrumentationServerManager;
import org.jruby.truffle.tools.callgraph.CallGraph;
import org.jruby.truffle.tools.callgraph.SimpleWriter;
@@ -91,6 +92,7 @@ public class RubyContext extends ExecutionContext {
private final CallGraph callGraph;
private final PrintStream debugStandardOut;
private final CoverageManager coverageManager;
private final CallFrequency callFrequency;

private final Object classVariableDefinitionLock = new Object();

@@ -152,6 +154,15 @@ public RubyContext(Ruby jrubyRuntime, TruffleLanguage.Env env) {
primitiveManager.addAnnotatedPrimitives();
org.jruby.Main.printTruffleTimeMetric("after-load-nodes");

// Systems which need to be loaded before we load Ruby core

if (options.CALL_FREQUENCY) {
callFrequency = new CallFrequency(this);
callFrequency.start();
} else {
callFrequency = null;
}

// Load the reset of the core library

coreLibrary.initializeAfterBasicMethodsAdded();
@@ -348,4 +359,7 @@ public CoreExceptions getCoreExceptions() {
return coreExceptions;
}

public CallFrequency getCallFrequency() {
return callFrequency;
}
}
2 changes: 2 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/language/Options.java
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@
import static org.jruby.util.cli.Options.TRUFFLE_BACKTRACES_OMIT_UNUSED;
import static org.jruby.util.cli.Options.TRUFFLE_BINDING_LOCAL_VARIABLE_CACHE;
import static org.jruby.util.cli.Options.TRUFFLE_BIND_CACHE;
import static org.jruby.util.cli.Options.TRUFFLE_CALL_FREQUENCY;
import static org.jruby.util.cli.Options.TRUFFLE_CALL_GRAPH;
import static org.jruby.util.cli.Options.TRUFFLE_CALL_GRAPH_WRITE;
import static org.jruby.util.cli.Options.TRUFFLE_CLASS_CACHE;
@@ -151,6 +152,7 @@ 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 CALL_FREQUENCY = TRUFFLE_CALL_FREQUENCY.load();

// Call graph

Original file line number Diff line number Diff line change
@@ -41,6 +41,10 @@ public Object dispatch(
Object methodName,
DynamicObject blockObject,
Object[] argumentsObjects) {
if (context.getOptions().CALL_FREQUENCY) {
context.getCallFrequency().called(receiverObject, methodName);
}

return first.executeDispatch(
frame,
receiverObject,
86 changes: 86 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/tools/CallFrequency.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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.tools;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

public class CallFrequency {

private final RubyContext context;
private final Map<String, AtomicLong> calls = new HashMap<>();

public CallFrequency(RubyContext context) {
this.context = context;
}

@TruffleBoundary
public void called(Object receiver, Object name) {
final DynamicObject metaClass = context.getCoreLibrary().getMetaClass(receiver);
final String formatted = Layouts.CLASS.getFields(metaClass).getName() + "#" + name;

AtomicLong count;

synchronized (this) {
count = calls.get(formatted);

if (count == null) {
count = new AtomicLong();
calls.put(formatted, count);
}
}

count.incrementAndGet();
}

public void start() {
final Thread printThread = new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Doesn't matter
}

synchronized (this) {
final List<Map.Entry<String, AtomicLong>> entries = new ArrayList<>(calls.entrySet());
entries.sort((a, b) -> Long.compare(a.getValue().get(), b.getValue().get()));
Collections.reverse(entries);

while (entries.size() > 10) {
entries.remove(10);
}

System.err.println(System.currentTimeMillis());

for (Map.Entry<String, AtomicLong> entry : entries) {
System.err.println(" " + entry.getValue().get() + " " + entry.getKey());
}

calls.clear();
}
}
});

printThread.setDaemon(true);
printThread.start();
}

}

0 comments on commit 7348b68

Please sign in to comment.