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: 35af9f8adfbe
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 804d0859d911
Choose a head ref
  • 14 commits
  • 3 files changed
  • 1 contributor

Commits on Apr 30, 2016

  1. Copy the full SHA
    4a42f75 View commit details
  2. [Truffle] Coverage methods shouldn't be defined on the singleton and …

    …should be extended by the user.
    chrisseaton committed Apr 30, 2016
    Copy the full SHA
    7535d3a View commit details
  3. Copy the full SHA
    bef85a9 View commit details
  4. Copy the full SHA
    b3da711 View commit details
  5. Copy the full SHA
    d14d768 View commit details

Commits on May 1, 2016

  1. [Truffle] Avoid creating Hashes from Java - move into a Ruby helper i…

    …nstead as it's a kerfuffle.
    chrisseaton committed May 1, 2016
    Copy the full SHA
    a988889 View commit details
  2. [Truffle] De-duplicate.

    chrisseaton committed May 1, 2016
    Copy the full SHA
    024dd73 View commit details
  3. Copy the full SHA
    66fd1f3 View commit details
  4. Copy the full SHA
    e06c50c View commit details
  5. Copy the full SHA
    65ca155 View commit details
  6. Copy the full SHA
    b831fce View commit details
  7. Copy the full SHA
    e96e8f3 View commit details
  8. Copy the full SHA
    33d2f89 View commit details
  9. Copy the full SHA
    804d085 View commit details
10 changes: 9 additions & 1 deletion lib/ruby/truffle/truffle/coverage.rb
Original file line number Diff line number Diff line change
@@ -7,5 +7,13 @@
# GNU Lesser General Public License version 2.1

module Coverage
include Truffle::Coverage

def self.start
Truffle::Coverage.start
end

def self.result
Truffle::Coverage.result_array.to_h
end

end
54 changes: 40 additions & 14 deletions truffle/src/main/java/org/jruby/truffle/stdlib/CoverageManager.java
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@

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.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.EventContext;
import com.oracle.truffle.api.instrumentation.ExecutionEventNode;
@@ -23,24 +24,25 @@
import org.jruby.truffle.RubyContext;

import java.io.PrintStream;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLongArray;

public class CoverageManager {
public @interface LineTag {

public class LineTag {
}

public static final long NO_CODE = -1;

private final Instrumenter instrumenter;

private boolean enabled = false;

private final Map<Source, AtomicLongArray> counters = new ConcurrentHashMap<>();
private final Map<Source, BitSet> codeMap = new HashMap<>();
private final Map<Source, BitSet> linesHaveCode = new HashMap<>();

private boolean enabled;

public CoverageManager(RubyContext context, Instrumenter instrumenter) {
this.instrumenter = instrumenter;
@@ -51,22 +53,25 @@ public CoverageManager(RubyContext context, Instrumenter instrumenter) {
}

public synchronized void setLineHasCode(LineLocation line) {
BitSet bitmap = codeMap.get(line.getSource());
BitSet bitmap = linesHaveCode.get(line.getSource());

if (bitmap == null) {
bitmap = new BitSet(line.getSource().getLineCount());
codeMap.put(line.getSource(), bitmap);
linesHaveCode.put(line.getSource(), bitmap);
}

bitmap.set(line.getLineNumber() - 1);
}

public void enable() {
@TruffleBoundary
public synchronized void enable() {
if (enabled) {
throw new UnsupportedOperationException();
return;
}

instrumenter.attachFactory(SourceSectionFilter.newBuilder().tagIs(LineTag.class).build(), new ExecutionEventNodeFactory() {
instrumenter.attachFactory(SourceSectionFilter.newBuilder()
.tagIs(LineTag.class)
.build(), new ExecutionEventNodeFactory() {

@Override
public ExecutionEventNode create(EventContext eventContext) {
@@ -110,7 +115,7 @@ public Map<Source, long[]> getCounts() {
final Map<Source, long[]> counts = new HashMap<>();

for (Map.Entry<Source, AtomicLongArray> entry : counters.entrySet()) {
final BitSet hasCode = codeMap.get(entry.getKey());
final BitSet hasCode = linesHaveCode.get(entry.getKey());

final long[] array = new long[entry.getValue().length()];

@@ -129,8 +134,17 @@ public Map<Source, long[]> getCounts() {
}

public void print(PrintStream out) {
final int maxCountDigits = Long.toString(getMaxCount()).length();

final String countFormat = "%" + maxCountDigits + "d";

final char[] noCodeChars = new char[maxCountDigits];
Arrays.fill(noCodeChars, ' ');
noCodeChars[maxCountDigits - 1] = '-';
final String noCodeString = new String(noCodeChars);

for (Map.Entry<Source, AtomicLongArray> entry : counters.entrySet()) {
final BitSet hasCode = codeMap.get(entry.getKey());
final BitSet hasCode = linesHaveCode.get(entry.getKey());

out.println(entry.getKey().getName());

@@ -144,14 +158,26 @@ public void print(PrintStream out) {
out.print(" ");

if (hasCode != null && hasCode.get(n)) {
out.printf("% 12d", entry.getValue().get(n));
out.printf(countFormat, entry.getValue().get(n));
} else {
out.print(" -");
out.print(noCodeString);
}

out.printf(" %s%n", line);
}
}
}

private long getMaxCount() {
long max = 0;

for (Map.Entry<Source, AtomicLongArray> entry : counters.entrySet()) {
for (int n = 0; n < entry.getValue().length(); n++) {
max = Math.max(max, entry.getValue().get(n));
}
}

return max;
}

}
47 changes: 21 additions & 26 deletions truffle/src/main/java/org/jruby/truffle/stdlib/CoverageNodes.java
Original file line number Diff line number Diff line change
@@ -18,19 +18,18 @@
import org.jruby.truffle.core.CoreMethod;
import org.jruby.truffle.core.CoreMethodArrayArgumentsNode;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.hash.BucketsStrategy;
import org.jruby.truffle.core.string.StringOperations;

import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@CoreClass(name = "Truffle::Coverage")
public abstract class CoverageNodes {

@CoreMethod(names = "coverage_start", onSingleton = true)
@CoreMethod(names = "start", onSingleton = true, needsSelf = false)
public abstract static class CoverageStartNode extends CoreMethodArrayArgumentsNode {

@TruffleBoundary
@Specialization
public DynamicObject coverageStart() {
getContext().getCoverageManager().enable();
@@ -39,42 +38,38 @@ public DynamicObject coverageStart() {

}

@CoreMethod(names = "coverage_result", onSingleton = true)
@CoreMethod(names = "result_array", onSingleton = true, needsSelf = false)
public abstract static class CoverageResultNode extends CoreMethodArrayArgumentsNode {

@TruffleBoundary
@Specialization
public DynamicObject coverageResult() {
if (getContext().getCoverageManager() == null) {
throw new UnsupportedOperationException("coverage is disabled");
}

final Map<Object, Object> converted = new HashMap<>();
final List<DynamicObject> results = new ArrayList<>();

for (Map.Entry<Source, long[]> source : getContext().getCoverageManager().getCounts().entrySet()) {
final Object[] store = lineCountsStore(source.getValue());
final DynamicObject array = Layouts.ARRAY.createArray(coreLibrary().getArrayFactory(), store, store.length);
final long[] countsArray = source.getValue();

if (source.getKey().getPath() != null) {
converted.put(createString(StringOperations.encodeRope(source.getKey().getPath(), UTF8Encoding.INSTANCE)), array);
}
}
final Object[] countsStore = new Object[countsArray.length];

return BucketsStrategy.create(getContext(), converted.entrySet(), false);
}
for (int n = 0; n < countsArray.length; n++) {
final Object countObject;

private Object[] lineCountsStore(long[] array) {
final Object[] store = new Object[array.length];
if (countsArray[n] == CoverageManager.NO_CODE) {
countObject = nil();
} else {
countObject = countsArray[n];
}

for (int n = 0; n < array.length; n++) {
if (array[n] == CoverageManager.NO_CODE) {
store[n] = nil();
} else {
store[n] = array[n];
countsStore[n] = countObject;
}

results.add(Layouts.ARRAY.createArray(coreLibrary().getArrayFactory(), new Object[]{
createString(StringOperations.encodeRope(source.getKey().getPath(), UTF8Encoding.INSTANCE)),
Layouts.ARRAY.createArray(coreLibrary().getArrayFactory(), countsStore, countsStore.length)
}, 2));
}

return store;
return Layouts.ARRAY.createArray(coreLibrary().getArrayFactory(), results.toArray(), results.size());
}

}