diff --git a/com.oracle.truffle.r.agents/src/com/oracle/truffle/r/agents/objsize/ObjSizeAgent.java b/com.oracle.truffle.r.agents/src/com/oracle/truffle/r/agents/objsize/ObjSizeAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..073d6bf9a9587b005d09f1d185aa4ff9f4cb4c65 --- /dev/null +++ b/com.oracle.truffle.r.agents/src/com/oracle/truffle/r/agents/objsize/ObjSizeAgent.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.agents.objsize; + +import java.lang.instrument.Instrumentation; + +public class ObjSizeAgent { + private static Instrumentation instrumentation; + + public static void agentmain(@SuppressWarnings("unused") String agentArgs, Instrumentation inst) { + instrumentation = inst; + } + + public static long objectSize(Object obj) { + return instrumentation.getObjectSize(obj); + } +} diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java index dc9ba058337ef8f48d23465a6b0f6085c07ffc49..362e8af2e20c8ed5f4c31f5487ccab7ef277860b 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java @@ -41,7 +41,6 @@ import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags; import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RAccuracyInfo; import com.oracle.truffle.r.runtime.RError; -import com.oracle.truffle.r.runtime.RPerfStats; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.RVersionInfo; import com.oracle.truffle.r.runtime.TempPathName; @@ -69,7 +68,6 @@ public final class TruffleRLanguage extends TruffleLanguage<RContext> { private static void initialize() { try { Load_RFFIFactory.initialize(true); - RPerfStats.initialize(); Locale.setDefault(Locale.ROOT); RAccuracyInfo.initialize(); RVersionInfo.initialize(); diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java index db38227316cc29e6eec6064ae342363e887c6018..7deff193de910b2d8bd3cd91b6f23fa2a49d0515 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java @@ -22,9 +22,9 @@ */ package com.oracle.truffle.r.library.utils; -import java.io.FileWriter; +import java.io.FileOutputStream; import java.io.IOException; -import java.io.PrintWriter; +import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.function.Function; @@ -57,8 +57,7 @@ import com.oracle.truffle.r.runtime.data.RTypedValue; import com.oracle.truffle.r.runtime.data.MemoryCopyTracer; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.data.model.RAbstractVector; -import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState; -import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState.MemoryQuad; +import com.oracle.truffle.r.runtime.instrument.InstrumentationState; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; /** @@ -79,36 +78,31 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; */ public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFactory.Listener, MemoryCopyTracer.Listener { - private RprofState profState; - @SuppressWarnings("unused") @Specialization public Object doRprof(RAbstractStringVector filenameVec, byte appendL, double intervalD, byte memProfilingL, byte gcProfilingL, byte lineProfilingL, int numFiles, int bufSize) { - if (!RContext.getInstance().isInitial()) { - throw RError.error(this, RError.Message.GENERIC, "profiling not supported in created contexts"); - } - profState = RContext.getInstance().stateInstrumentation.getRprof(); + RprofState profState = RprofState.get(); String filename = filenameVec.getDataAt(0); if (filename.length() == 0) { // disable endProfiling(); } else { // enable after ending any previous session - if (profState.out() != null) { + if (profState != null && profState.out() != null) { endProfiling(); } boolean append = RRuntime.fromLogical(appendL); boolean memProfiling = RRuntime.fromLogical(memProfilingL); boolean gcProfiling = RRuntime.fromLogical(gcProfilingL); try { - PrintWriter out = new PrintWriter(new FileWriter(filename, append)); + PrintStream out = new PrintStream(new FileOutputStream(filename, append)); if (gcProfiling) { RError.warning(this, RError.Message.GENERIC, "Rprof: gc profiling not supported"); } if (memProfiling) { RDataFactory.addListener(this); - RDataFactory.setAllocationTracing(true); + RDataFactory.setTracingState(true); MemoryCopyTracer.addListener(this); MemoryCopyTracer.setTracingState(true); } @@ -129,15 +123,16 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFa @Override @TruffleBoundary public void reportAllocation(RTypedValue data) { + RprofState profState = RprofState.get(); long size = RObjectSize.getObjectSize(data, Rprofmem.myIgnoreObjectHandler); if (data instanceof RAbstractVector) { if (size >= Rprofmem.LARGE_VECTOR) { - profState.memoryQuad().largeV += size; + profState.memoryQuad.largeV += size; } else { - profState.memoryQuad().smallV += size; + profState.memoryQuad.smallV += size; } } else { - profState.memoryQuad().nodes += size; + profState.memoryQuad.nodes += size; } } @@ -145,65 +140,15 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFa @Override @TruffleBoundary public void reportCopying(RAbstractVector source, RAbstractVector dest) { - profState.memoryQuad().copied += RObjectSize.getObjectSize(source, Rprofmem.myIgnoreObjectHandler); + RprofState profState = RprofState.get(); + profState.memoryQuad.copied += RObjectSize.getObjectSize(source, Rprofmem.myIgnoreObjectHandler); } - private void endProfiling() { - ProfileThread profileThread = (ProfileThread) profState.profileThread(); - profileThread.running = false; - HashMap<String, Integer> fileMap = null; - PrintWriter out = profState.out(); - StatementListener statementListener = (StatementListener) profState.statementListener(); - if (profState.memoryProfiling()) { - out.print("memory profiling: "); + private static void endProfiling() { + RprofState profState = RprofState.get(); + if (profState.out() != null) { + profState.cleanup(0); } - if (profState.lineProfiling()) { - out.print("line profiling: "); - } - out.printf("sample.interval=%d\n", profState.intervalInMillis() * 1000); - if (profState.lineProfiling()) { - // scan stacks to find files - fileMap = new HashMap<>(); - int fileIndex = 0; - for (ArrayList<RSyntaxNode> intervalStack : statementListener.intervalStacks) { - for (RSyntaxNode node : intervalStack) { - String path = getPath(node); - if (path != null && fileMap.get(path) == null) { - fileMap.put(path, ++fileIndex); - out.printf("#File %d: %s\n", fileIndex, path); - } - } - } - } - int index = 0; - for (ArrayList<RSyntaxNode> intervalStack : statementListener.intervalStacks) { - if (profState.memoryProfiling()) { - MemoryQuad mq = statementListener.intervalMemory.get(index); - out.printf(":%d:%d:%d:%d:", mq.largeV, mq.smallV, mq.nodes, mq.copied); - } - for (RSyntaxNode node : intervalStack) { - RootNode rootNode = node.asRNode().getRootNode(); - if (rootNode instanceof FunctionDefinitionNode) { - String name = rootNode.getName(); - if (profState.lineProfiling()) { - Integer fileIndex = fileMap.get(getPath(node)); - if (fileIndex != null) { - out.printf("%d#%d ", fileIndex, node.getSourceSection().getStartLine()); - } - } - out.printf("\"%s\" ", name); - } - } - out.println(); - index++; - } - out.close(); - profState.setOut(null); - if (profState.memoryProfiling()) { - RDataFactory.setAllocationTracing(false); - MemoryCopyTracer.setTracingState(false); - } - } private static String getPath(RSyntaxNode node) { @@ -242,7 +187,7 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFa */ private final class StatementListener implements ExecutionEventListener { private ArrayList<ArrayList<RSyntaxNode>> intervalStacks = new ArrayList<>(); - private ArrayList<MemoryQuad> intervalMemory = new ArrayList<>(); + private ArrayList<RprofState.MemoryQuad> intervalMemory = new ArrayList<>(); private volatile boolean newInterval; private StatementListener() { @@ -264,8 +209,9 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFa stack.add((RSyntaxNode) context.getInstrumentedNode()); collectStack(stack); intervalStacks.add(stack); - if (profState.memoryProfiling()) { - intervalMemory.add(profState.memoryQuad().copyAndClear()); + RprofState profState = RprofState.get(); + if (profState.memoryProfiling) { + intervalMemory.add(profState.memoryQuad.copyAndClear()); } newInterval = false; @@ -301,4 +247,115 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFa } } + + /** + * State used by {@code Rprof}. + * + */ + private static final class RprofState extends InstrumentationState.RprofState { + private ProfileThread profileThread; + private StatementListener statementListener; + private long intervalInMillis; + private boolean lineProfiling; + private boolean memoryProfiling; + private MemoryQuad memoryQuad; + + public static final class MemoryQuad { + public long smallV; + public long largeV; + public long nodes; + public long copied; + + public MemoryQuad copyAndClear() { + MemoryQuad result = new MemoryQuad(); + result.copied = copied; + result.largeV = largeV; + result.smallV = smallV; + result.nodes = nodes; + copied = 0; + largeV = 0; + smallV = 0; + nodes = 0; + return result; + } + } + + private static RprofState get() { + RprofState state = (RprofState) RContext.getInstance().stateInstrumentation.getRprofState("prof"); + if (state == null) { + state = new RprofState(); + RContext.getInstance().stateInstrumentation.setRprofState("prof", state); + } + return state; + } + + public void initialize(PrintStream outA, ProfileThread profileThreadA, StatementListener statementListenerA, long intervalInMillisA, + boolean lineProfilingA, boolean memoryProfilingA) { + setOut(outA); + this.profileThread = profileThreadA; + this.statementListener = statementListenerA; + this.intervalInMillis = intervalInMillisA; + this.lineProfiling = lineProfilingA; + this.memoryProfiling = memoryProfilingA; + this.memoryQuad = memoryProfilingA ? new MemoryQuad() : null; + } + + @Override + public void cleanup(int status) { + profileThread.running = false; + HashMap<String, Integer> fileMap = null; + PrintStream out = this.out(); + if (this.memoryProfiling) { + out.print("memory profiling: "); + } + if (this.lineProfiling) { + out.print("line profiling: "); + } + out.printf("sample.interval=%d\n", this.intervalInMillis * 1000); + if (this.lineProfiling) { + // scan stacks to find files + fileMap = new HashMap<>(); + int fileIndex = 0; + for (ArrayList<RSyntaxNode> intervalStack : statementListener.intervalStacks) { + for (RSyntaxNode node : intervalStack) { + String path = getPath(node); + if (path != null && fileMap.get(path) == null) { + fileMap.put(path, ++fileIndex); + out.printf("#File %d: %s\n", fileIndex, path); + } + } + } + } + int index = 0; + for (ArrayList<RSyntaxNode> intervalStack : statementListener.intervalStacks) { + if (this.memoryProfiling) { + RprofState.MemoryQuad mq = statementListener.intervalMemory.get(index); + out.printf(":%d:%d:%d:%d:", mq.largeV, mq.smallV, mq.nodes, mq.copied); + } + for (RSyntaxNode node : intervalStack) { + RootNode rootNode = node.asRNode().getRootNode(); + if (rootNode instanceof FunctionDefinitionNode) { + String name = rootNode.getName(); + if (this.lineProfiling) { + Integer fileIndex = fileMap.get(getPath(node)); + if (fileIndex != null) { + out.printf("%d#%d ", fileIndex, node.getSourceSection().getStartLine()); + } + } + out.printf("\"%s\" ", name); + } + } + out.println(); + index++; + } + out.close(); + this.setOut(null); + if (this.memoryProfiling) { + RDataFactory.setTracingState(false); + MemoryCopyTracer.setTracingState(false); + } + + } + + } } diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java index 5fa5c672e50bcf65325c39085cb018110c0dc822..b88be4b9d9563568d5a0994910a3ed8b595e13c4 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java @@ -22,10 +22,9 @@ */ package com.oracle.truffle.r.library.utils; -import java.io.FileWriter; +import java.io.FileOutputStream; import java.io.IOException; -import java.io.PrintWriter; - +import java.io.PrintStream; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; @@ -44,34 +43,29 @@ import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.data.model.RAbstractVector; -import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofmemState; +import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState; public abstract class Rprofmem extends RExternalBuiltinNode.Arg3 implements RDataFactory.Listener { - private RprofmemState profmemState; - @Specialization @TruffleBoundary public Object doRprofmem(RAbstractStringVector filenameVec, byte appendL, RAbstractDoubleVector thresholdVec) { - if (!RContext.getInstance().isInitial()) { - throw RError.error(this, RError.Message.GENERIC, "profiling not supported in created contexts"); - } String filename = filenameVec.getDataAt(0); if (filename.length() == 0) { // disable endProfiling(); } else { // enable after ending any previous session - profmemState = RContext.getInstance().stateInstrumentation.getRprofmem(); - if (profmemState.out() != null) { + RprofmemState profmemState = RprofmemState.get(); + if (profmemState != null && profmemState.out() != null) { endProfiling(); } boolean append = RRuntime.fromLogical(appendL); try { - PrintWriter out = new PrintWriter(new FileWriter(filename, append)); + PrintStream out = new PrintStream(new FileOutputStream(filename, append)); profmemState.initialize(out, thresholdVec.getDataAt(0)); RDataFactory.addListener(this); - RDataFactory.setAllocationTracing(true); + RDataFactory.setTracingState(true); } catch (IOException ex) { throw RError.error(this, RError.Message.GENERIC, String.format("Rprofmem: cannot open profile file '%s'", filename)); } @@ -79,12 +73,10 @@ public abstract class Rprofmem extends RExternalBuiltinNode.Arg3 implements RDat return RNull.instance; } - private void endProfiling() { - if (profmemState != null) { - RDataFactory.setAllocationTracing(false); - profmemState.out().flush(); - profmemState.out().close(); - profmemState.setOut(null); + private static void endProfiling() { + RprofmemState profmemState = RprofmemState.get(); + if (profmemState.out() != null) { + profmemState.cleanup(0); } } @@ -119,6 +111,7 @@ public abstract class Rprofmem extends RExternalBuiltinNode.Arg3 implements RDat public void reportAllocation(RTypedValue data) { // We could do some in memory buffering // TODO write out full stack + RprofmemState profmemState = RprofmemState.get(); Frame frame = Utils.getActualCurrentFrame(); if (frame == null) { // not an R evaluation, some internal use @@ -132,18 +125,46 @@ public abstract class Rprofmem extends RExternalBuiltinNode.Arg3 implements RDat long size = RObjectSize.getObjectSize(data, myIgnoreObjectHandler); if (data instanceof RAbstractVector && size >= LARGE_VECTOR) { - profmemState.out().printf("%d: %s\n", size, name); + RAbstractVector absv = (RAbstractVector) data; + if (size > profmemState.threshold) { + profmemState.out().printf("%d: %s\n", size, name); + } } else { - int pageCount = profmemState.pageCount(); + int pageCount = profmemState.pageCount; long pcs = pageCount + size; if (pcs > PAGE_SIZE) { profmemState.out().printf("new page: %s\n", name); - profmemState.setPageCount((int) (pcs - PAGE_SIZE)); + profmemState.pageCount = (int) (pcs - PAGE_SIZE); } else { - profmemState.setPageCount((int) pcs); + profmemState.pageCount = (int) pcs; + } + } + + } + + private static final class RprofmemState extends RprofState { + private double threshold; + private int pageCount; + + private static RprofmemState get() { + RprofmemState state = (RprofmemState) RContext.getInstance().stateInstrumentation.getRprofState("mem"); + if (state == null) { + state = new RprofmemState(); + RContext.getInstance().stateInstrumentation.setRprofState("mem", state); } + return state; } + public void initialize(PrintStream outA, double thresholdA) { + setOut(outA); + this.threshold = thresholdA; + } + + @Override + public void cleanup(int status) { + RDataFactory.setTracingState(false); + closeAndResetOut(); + } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java index 8170e1d1b19f680b8335398e890fe242f5ef98cb..842c52397077ded5530639fd9aa1201a406e55a3 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java @@ -48,8 +48,6 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastRContext; import com.oracle.truffle.r.nodes.builtin.fastr.FastRContextFactory; import com.oracle.truffle.r.nodes.builtin.fastr.FastRDebug; import com.oracle.truffle.r.nodes.builtin.fastr.FastRDebugNodeGen; -import com.oracle.truffle.r.nodes.builtin.fastr.FastRFunctionProfiler; -import com.oracle.truffle.r.nodes.builtin.fastr.FastRFunctionProfilerFactory; import com.oracle.truffle.r.nodes.builtin.fastr.FastRIdentity; import com.oracle.truffle.r.nodes.builtin.fastr.FastRIdentityNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRInspect; @@ -60,6 +58,10 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastRRefCountInfo; import com.oracle.truffle.r.nodes.builtin.fastr.FastRRefCountInfoNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRStackTrace; import com.oracle.truffle.r.nodes.builtin.fastr.FastRStackTraceNodeGen; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRStats.FastRProfAttr; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRStats.FastRProfFuncounts; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRStats.FastRProfTypecounts; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRStatsFactory; import com.oracle.truffle.r.nodes.builtin.fastr.FastRSyntaxTree; import com.oracle.truffle.r.nodes.builtin.fastr.FastRSyntaxTreeNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRThrowIt; @@ -289,10 +291,6 @@ public class BasePackage extends RBuiltinPackage { add(FastRContext.Join.class, FastRContextFactory.JoinNodeGen::create); add(FastrDqrls.class, FastrDqrlsNodeGen::create); add(FastRDebug.class, FastRDebugNodeGen::create); - add(FastRFunctionProfiler.Create.class, FastRFunctionProfilerFactory.CreateNodeGen::create); - add(FastRFunctionProfiler.Get.class, FastRFunctionProfilerFactory.GetNodeGen::create); - add(FastRFunctionProfiler.Reset.class, FastRFunctionProfilerFactory.ResetNodeGen::create); - add(FastRFunctionProfiler.Clear.class, FastRFunctionProfilerFactory.ClearNodeGen::create); add(FastRIdentity.class, FastRIdentityNodeGen::create); add(FastRInspect.class, FastRInspectNodeGen::create); add(FastRInterop.Eval.class, FastRInteropFactory.EvalNodeGen::create); @@ -300,6 +298,9 @@ public class BasePackage extends RBuiltinPackage { add(FastRInterop.Import.class, FastRInteropFactory.ImportNodeGen::create); add(FastRRefCountInfo.class, FastRRefCountInfoNodeGen::create); add(FastRStackTrace.class, FastRStackTraceNodeGen::create); + add(FastRProfAttr.class, FastRStatsFactory.FastRProfAttrNodeGen::create); + add(FastRProfTypecounts.class, FastRStatsFactory.FastRProfTypecountsNodeGen::create); + add(FastRProfFuncounts.class, FastRStatsFactory.FastRProfFuncountsNodeGen::create); add(FastRSyntaxTree.class, FastRSyntaxTreeNodeGen::create); add(FastRThrowIt.class, FastRThrowItNodeGen::create); add(FastRTrace.Trace.class, FastRTraceFactory.TraceNodeGen::create); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java index ae7d75ffc1eff9aaa109ff114b61611e4fff620b..d4e2fe9c14e54bcbd422b3c076807c755cc37efe 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java @@ -348,4 +348,5 @@ public class FastRContext { } } } + } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionProfiler.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionProfiler.java deleted file mode 100644 index 580a10472010aacda10f4bc56192bca9b2f80c1a..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionProfiler.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.r.nodes.builtin.fastr; - -import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleValue; -import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf; -import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue; -import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.missingValue; -import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue; -import static com.oracle.truffle.r.runtime.RVisibility.OFF; -import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; -import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE; - -import java.util.ArrayList; - -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.r.nodes.builtin.CastBuilder; -import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; -import com.oracle.truffle.r.nodes.instrumentation.RFunctionProfiler; -import com.oracle.truffle.r.runtime.RError; -import com.oracle.truffle.r.runtime.RInternalError; -import com.oracle.truffle.r.runtime.RRuntime; -import com.oracle.truffle.r.runtime.builtins.RBuiltin; -import com.oracle.truffle.r.runtime.data.RDataFactory; -import com.oracle.truffle.r.runtime.data.RDoubleVector; -import com.oracle.truffle.r.runtime.data.RFunction; -import com.oracle.truffle.r.runtime.data.RList; -import com.oracle.truffle.r.runtime.data.RMissing; -import com.oracle.truffle.r.runtime.data.RNull; -import com.oracle.truffle.r.runtime.data.RStringVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; -import com.oracle.truffle.tools.Profiler; - -public class FastRFunctionProfiler { - - @RBuiltin(name = ".fastr.profiler.create", visibility = OFF, kind = PRIMITIVE, parameterNames = {"func", "mode"}, behavior = COMPLEX) - public abstract static class Create extends RBuiltinNode { - private static final int COUNTING = 1; - private static final int TIMING = 2; - - @Override - public Object[] getDefaultParameterValues() { - return new Object[]{RMissing.instance, "counting"}; - } - - @Override - protected void createCasts(CastBuilder casts) { - casts.arg("func").mustBe(instanceOf(RFunction.class).or(missingValue())); - - casts.arg("mode").mustBe(stringValue()).asStringVector(); - } - - private int checkMode(RAbstractStringVector modeVec) { - int result = 0; - for (int i = 0; i < modeVec.getLength(); i++) { - String mode = modeVec.getDataAt(i); - switch (mode) { - case "counting": - result |= COUNTING; - break; - case "timing": - result |= TIMING; - break; - default: - throw RError.error(this, RError.Message.GENERIC, "invalid 'mode', one of 'count, timning' expected"); - } - } - return result; - } - - @Specialization - @TruffleBoundary - protected RNull createFunctionProfiler(RFunction function, RAbstractStringVector modeVec) { - int mode = checkMode(modeVec); - if (!function.isBuiltin()) { - RFunctionProfiler.installTimer(function, (mode & COUNTING) != 0, (mode & TIMING) != 0); - } else { - throw RError.error(this, RError.Message.GENERIC, "cannot profile builtin functions"); - } - return RNull.instance; - } - - @Specialization - @TruffleBoundary - protected RNull createFunctionProfiler(@SuppressWarnings("unused") RMissing value, RAbstractStringVector modeVec) { - int mode = checkMode(modeVec); - RFunctionProfiler.installTimer(null, (mode & COUNTING) != 0, (mode & TIMING) != 0); - return RNull.instance; - } - - } - - @RBuiltin(name = ".fastr.profiler.get", kind = PRIMITIVE, parameterNames = {"func", "threshold", "scale"}, behavior = COMPLEX) - public abstract static class Get extends RBuiltinNode { - - private static final RStringVector COLNAMES = RDataFactory.createStringVector(new String[]{"Invocations", "TotalTime", "SelfTime"}, RDataFactory.COMPLETE_VECTOR); - private static final RStringVector ROWNAMES = RDataFactory.createStringVector(new String[]{"Combined", "Interpreted", "Compiled"}, RDataFactory.COMPLETE_VECTOR); - private static final int NCOLS = 3; - private static final int NROWS = 3; - - @Override - protected void createCasts(CastBuilder casts) { - casts.arg("func").mustBe(instanceOf(RFunction.class).or(missingValue())); - - casts.arg("threshold").mustBe(integerValue().or(doubleValue())).asDoubleVector(); - - casts.arg("scale").mustBe(stringValue()).asStringVector(); - } - - @Override - public Object[] getDefaultParameterValues() { - return new Object[]{RMissing.instance, 0.0, "nanos"}; - } - - private void checkScale(String s) throws RError { - if (!(s.equals("nanos") || s.equals("millis") || s.equals("micros") || s.equals("secs"))) { - throw RError.error(this, RError.Message.GENERIC, "invalid scale: one of 'nanos, micros, millis, secs' expected"); - } - } - - @Specialization - @TruffleBoundary - protected Object get(@SuppressWarnings("unused") RMissing value, RAbstractDoubleVector thresholdVec, RAbstractStringVector scaleVec) { - String scale = scaleVec.getDataAt(0); - checkScale(scale); - double threshold = thresholdVec.getDataAt(0); - Profiler.Counter[] counters = RFunctionProfiler.getCounters(); - if (counters == null) { - throw RError.error(this, RError.Message.GENERIC, "profiling not enabled"); - } - ArrayList<RDoubleVector> dataList = new ArrayList<>(); - ArrayList<String> nameList = new ArrayList<>(); - for (int i = 0; i < counters.length; i++) { - Profiler.Counter counter = counters[i]; - if (threshold > 0.0) { - long time = counter.getTotalTime(Profiler.Counter.TimeKind.INTERPRETED_AND_COMPILED); - if (time <= threshold) { - continue; - } - } - dataList.add(getFunctionMatrix(counter, scale)); - nameList.add(counter.getName()); - } - Object[] data = new Object[dataList.size()]; - String[] names = new String[nameList.size()]; - return RDataFactory.createList(dataList.toArray(data), RDataFactory.createStringVector(nameList.toArray(names), RDataFactory.COMPLETE_VECTOR)); - } - - @Specialization - @TruffleBoundary - protected Object get(RFunction function, @SuppressWarnings("unused") RAbstractDoubleVector threshold, RAbstractStringVector scaleVec) { - String scale = scaleVec.getDataAt(0); - checkScale(scale); - if (!function.isBuiltin()) { - Profiler.Counter counter = RFunctionProfiler.getCounter(function); - if (counter == null) { - throw RError.error(this, RError.Message.GENERIC, "profiling not enabled"); - } else { - return getFunctionMatrix(counter, scale); - } - } else { - throw RError.error(this, RError.Message.GENERIC, "cannot profile builtin functions"); - } - } - - private static RDoubleVector getFunctionMatrix(Profiler.Counter counter, String scale) { - double[] data = new double[NROWS * NCOLS]; - boolean isTiming = RFunctionProfiler.isTiming(); - boolean complete = isTiming ? RDataFactory.COMPLETE_VECTOR : RDataFactory.INCOMPLETE_VECTOR; - for (int r = 0; r < NROWS; r++) { - Profiler.Counter.TimeKind timeKind = Profiler.Counter.TimeKind.values()[r]; - for (int c = 0; c < NCOLS; c++) { - int index = c * NROWS + r; - double value = 0.0; - switch (c) { - case 0: - value = counter.getInvocations(timeKind); - break; - case 1: - value = isTiming ? counter.getTotalTime(timeKind) : RRuntime.DOUBLE_NA; - break; - case 2: - value = isTiming ? counter.getSelfTime(timeKind) : RRuntime.DOUBLE_NA; - } - data[index] = c == 0 ? value : scaledTime(scale, value); - } - } - RDoubleVector result = RDataFactory.createDoubleVector(data, complete, new int[]{3, 3}); - Object[] dimNamesData = new Object[2]; - dimNamesData[0] = ROWNAMES; - dimNamesData[1] = COLNAMES; - RList dimNames = RDataFactory.createList(dimNamesData); - result.setDimNames(dimNames); - return result; - } - - private static double scaledTime(String scale, double time) { - if (RRuntime.isNA(time)) { - return time; - } - switch (scale) { - case "nanos": - return time; - case "micros": - return time / 1000.0; - case "millis": - return time / 1000000.0; - case "secs": - return time / 1000000000.0; - default: - throw RInternalError.shouldNotReachHere(); - } - } - } - - @RBuiltin(name = ".fastr.profiler.reset", visibility = OFF, kind = PRIMITIVE, parameterNames = {}, behavior = COMPLEX) - public abstract static class Reset extends RBuiltinNode { - @Specialization - @TruffleBoundary - protected Object reset() { - RFunctionProfiler.reset(); - return RNull.instance; - } - } - - @RBuiltin(name = ".fastr.profiler.clear", visibility = OFF, kind = PRIMITIVE, parameterNames = {}, behavior = COMPLEX) - public abstract static class Clear extends RBuiltinNode { - @Specialization - @TruffleBoundary - protected Object clear() { - RFunctionProfiler.clear(); - return RNull.instance; - } - } -} diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRStats.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRStats.java new file mode 100644 index 0000000000000000000000000000000000000000..ab13f89bcefa882aa7611db5bbafd560915745fa --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRStats.java @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.builtin.fastr; + +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*; +import static com.oracle.truffle.r.runtime.RVisibility.OFF; +import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; +import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; +import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.RSource; +import com.oracle.truffle.r.runtime.builtins.RBuiltin; +import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.data.RAttributes; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RNull; +import com.oracle.truffle.r.runtime.data.RTypedValue; +import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractVector; +import com.oracle.truffle.r.runtime.data.RAttributes.AttributeTracer.Change; +import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState; +import com.oracle.truffle.tools.Profiler; +import com.oracle.truffle.tools.Profiler.Counter.TimeKind; + +public class FastRStats { + + private abstract static class CastHelper extends RBuiltinNode { + protected void filename(CastBuilder casts) { + casts.arg("filename").mustBe(stringValue().or(nullValue())).asStringVector(); + } + + protected void append(CastBuilder casts) { + casts.arg("append").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).notNA().map(toBoolean()); + } + } + + @RBuiltin(name = ".fastr.prof.attr", visibility = OFF, kind = PRIMITIVE, parameterNames = {"filename", "append"}, behavior = COMPLEX) + public abstract static class FastRProfAttr extends CastHelper implements RAttributes.AttributeTracer.Listener { + @Override + public Object[] getDefaultParameterValues() { + return new Object[]{"Rprofattr.out", RRuntime.LOGICAL_FALSE}; + } + + @Override + protected void createCasts(CastBuilder casts) { + filename(casts); + append(casts); + } + + @SuppressWarnings("unused") + @Specialization + @TruffleBoundary + protected RNull profAttr(RNull filenameVec, boolean append) { + endProfiling(); + return RNull.instance; + } + + @Specialization + @TruffleBoundary + protected RNull profAttr(RAbstractStringVector filenameVec, boolean append) { + if (filenameVec.getLength() == 0) { + // disable + endProfiling(); + } else { + // enable after ending any previous session + State state = State.get(); + try { + PrintStream out = new PrintStream(new FileOutputStream(filenameVec.getDataAt(0), append)); + state.setOut(out); + RAttributes.AttributeTracer.addListener(this); + RAttributes.AttributeTracer.setTracingState(true); + } catch (IOException ex) { + throw RError.error(this, RError.Message.GENERIC, String.format("Rprofmem: cannot open profile file '%s'", filenameVec.getDataAt(0))); + } + + } + return RNull.instance; + } + + protected void endProfiling() { + State state = State.get(); + if (state.out() != null) { + state.cleanup(0); + } + } + + @Override + public void reportAttributeChange(Change change, RAttributes attrs, Object value) { + State rprofattrState = State.get(); + rprofattrState.out().printf("%s,%d,", change.name(), System.identityHashCode(attrs)); + switch (change) { + case CREATE: + rprofattrState.out().print("NA"); + break; + case GROW: + rprofattrState.out().printf("%d", (int) value); + break; + default: + rprofattrState.out().printf("%s", (String) value); + break; + } + rprofattrState.out().println(); + } + + private static class State extends RprofState { + private static State get() { + State state = (State) RContext.getInstance().stateInstrumentation.getRprofState("attr"); + if (state == null) { + state = new State(); + RContext.getInstance().stateInstrumentation.setRprofState("attr", state); + } + return state; + } + + @Override + public void cleanup(int status) { + RAttributes.AttributeTracer.setTracingState(false); + closeAndResetOut(); + } + + } + + } + + @RBuiltin(name = ".fastr.stats.typecounts", visibility = OFF, kind = PRIMITIVE, parameterNames = {"filename", "append"}, behavior = COMPLEX) + public abstract static class FastRProfTypecounts extends CastHelper implements RDataFactory.Listener { + @Override + public Object[] getDefaultParameterValues() { + return new Object[]{"Rproftypecounts.out", RRuntime.LOGICAL_FALSE}; + } + + @Override + protected void createCasts(CastBuilder casts) { + filename(casts); + append(casts); + } + + @SuppressWarnings("unused") + @Specialization + @TruffleBoundary + protected RNull profTypecounts(RNull filenameVec, boolean append) { + endProfiling(); + return RNull.instance; + } + + @Specialization + @TruffleBoundary + protected RNull profTypecounts(RAbstractStringVector filenameVec, boolean append) { + if (filenameVec.getLength() == 0) { + // disable + endProfiling(); + } else { + // enable after ending any previous session + State state = State.get(); + try { + PrintStream out = new PrintStream(new FileOutputStream(filenameVec.getDataAt(0), append)); + state.setOut(out); + RDataFactory.addListener(this); + RDataFactory.setTracingState(true); + } catch (IOException ex) { + throw RError.error(this, RError.Message.GENERIC, String.format("Rprofmem: cannot open profile file '%s'", filenameVec.getDataAt(0))); + } + + } + return RNull.instance; + } + + protected void endProfiling() { + State state = State.get(); + if (state.out() != null) { + RDataFactory.setTracingState(false); + state.cleanup(0); + } + } + + @Override + public void reportAllocation(RTypedValue data) { + Class<? extends RTypedValue> klass = data.getClass(); + boolean isVector = (data instanceof RAbstractVector); + State state = State.get(); + Map<Class<? extends RTypedValue>, SortedMap<Integer, State.Counter>> typecountsMap = state.getTypecountsMap(); + SortedMap<Integer, State.Counter> countsMap = typecountsMap.get(klass); + if (countsMap == null) { + countsMap = new TreeMap<>(); + typecountsMap.put(klass, countsMap); + } + int length; + if (isVector) { + RAbstractVector vector = (RAbstractVector) data; + length = vector.getLength(); + } else { + length = 1; + } + State.Counter count = countsMap.get(length); + if (count == null) { + count = new State.Counter(); + countsMap.put(length, count); + } + count.incCount(); + } + + private static class State extends RprofState { + public static class Counter { + private int count; + + public void incCount() { + count++; + } + + public int getCount() { + return count; + } + + @Override + public String toString() { + return Integer.toString(count); + } + + } + + private Map<Class<? extends RTypedValue>, SortedMap<Integer, Counter>> typecountsMap; + + private static State get() { + State state = (State) RContext.getInstance().stateInstrumentation.getRprofState("typecounts"); + if (state == null) { + state = new State(); + RContext.getInstance().stateInstrumentation.setRprofState("typecounts", state); + } + return state; + } + + private Map<Class<? extends RTypedValue>, SortedMap<Integer, Counter>> getTypecountsMap() { + if (typecountsMap == null) { + typecountsMap = new HashMap<>(); + } + return typecountsMap; + } + + @Override + public void cleanup(int status) { + for (Map.Entry<Class<? extends RTypedValue>, SortedMap<Integer, Counter>> entry : getTypecountsMap().entrySet()) { + SortedMap<Integer, Counter> countsMap = entry.getValue(); + int totalCount = 0; + for (Counter counter : countsMap.values()) { + totalCount += counter.getCount(); + } + out().printf("%s,%d", entry.getKey().getSimpleName(), totalCount); + for (Map.Entry<Integer, Counter> countsEntry : countsMap.entrySet()) { + out().printf(",%d:%d", countsEntry.getKey(), countsEntry.getValue().getCount()); + } + out().println(); + } + closeAndResetOut(); + } + } + + } + + @RBuiltin(name = ".fastr.stats.funcounts", visibility = OFF, kind = PRIMITIVE, parameterNames = {"filename", "append", "timing", "threshold", "histograms"}, behavior = COMPLEX) + public abstract static class FastRProfFuncounts extends CastHelper { + @Override + public Object[] getDefaultParameterValues() { + return new Object[]{"Rproffuncounts.out", RRuntime.LOGICAL_FALSE, RRuntime.LOGICAL_FALSE, 0, RRuntime.LOGICAL_FALSE}; + } + + @Override + protected void createCasts(CastBuilder casts) { + filename(casts); + append(casts); + casts.arg("timing").asLogicalVector().findFirst().notNA().map(toBoolean()); + casts.arg("threshold").asIntegerVector().findFirst().notNA(); + casts.arg("histograms").asLogicalVector().findFirst().notNA().map(toBoolean()); + } + + @SuppressWarnings("unused") + @Specialization + @TruffleBoundary + protected RNull profFuncounts(RNull filenameVec, boolean append, boolean timing, int threshold, boolean histograms) { + endProfiling(); + return RNull.instance; + } + + @Specialization + @TruffleBoundary + protected RNull profFuncounts(RAbstractStringVector filenameVec, boolean append, boolean timing, int threshold, boolean histograms) { + if (filenameVec.getLength() == 0) { + // disable + endProfiling(); + } else { + // enable after ending any previous session + State state = State.get(); + try { + PrintStream out = new PrintStream(new FileOutputStream(filenameVec.getDataAt(0), append)); + state.initialize(out, threshold, timing, histograms); + Profiler profiler = RContext.getInstance().stateInstrumentation.getProfiler(); + profiler.setCollecting(true); + profiler.setTiming(timing); + } catch (IOException ex) { + throw RError.error(this, RError.Message.GENERIC, String.format("Rprofmem: cannot open profile file '%s'", filenameVec.getDataAt(0))); + } + + } + return RNull.instance; + } + + protected void endProfiling() { + State state = State.get(); + if (state.out() != null) { + state.cleanup(0); + } + } + + private static class State extends RprofState { + private int threshold; + private boolean timing; + private boolean histograms; + + private static State get() { + State state = (State) RContext.getInstance().stateInstrumentation.getRprofState("funcounts"); + if (state == null) { + state = new State(); + RContext.getInstance().stateInstrumentation.setRprofState("funcounts", state); + } + return state; + } + + private void initialize(PrintStream outA, int thresholdA, boolean timingA, boolean histogramsA) { + this.setOut(outA); + this.threshold = thresholdA; + this.timing = timingA; + this.histograms = histogramsA; + } + + @Override + public void cleanup(int status) { + Profiler profiler = RContext.getInstance().getInstrumentationState().getProfiler(); + if (histograms) { + profiler.printHistograms(out()); + return; + } + /* + * Report the statement counts/timing information at the end of the run. The report + * is per function. If timing is on, output is sorted by time, otherwise by + * invocation count. When timing, functions that consumed less time than requested + * threshold (default 0) are not included in the report. + */ + Map<SourceSection, Profiler.Counter> counters = profiler.getCounters(); + long totalTime = 0; + SortableCounter[] sortedCounters = new SortableCounter[counters.size()]; + int i = 0; + for (Profiler.Counter counter : counters.values()) { + sortedCounters[i++] = new SortableCounter(counter, timing); + if (timing) { + totalTime += counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED); + } + } + Arrays.sort(sortedCounters); + for (SortableCounter scounter : sortedCounters) { + long time = scounter.counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED); + long invocations = scounter.counter.getInvocations(TimeKind.INTERPRETED_AND_COMPILED); + boolean include = timing ? time > 0 && time > threshold : invocations > 0; + if (include) { + SourceSection ss = scounter.counter.getSourceSection(); + Source source = ss.getSource(); + out().println("=========="); + out().printf("calls %d", invocations); + if (timing) { + double thisPercent = percent(time, totalTime); + out().printf(", time %d ms (%.2f%%)", time, thisPercent); + } + out().printf(": %s, %s%n", scounter.counter.getName(), RSource.getOrigin(source)); + } + } + profiler.clearData(); + closeAndResetOut(); + } + + private static double percent(long a, long b) { + return ((double) a * 100) / b; + } + + } + + private static final class SortableCounter implements Comparable<SortableCounter> { + private boolean timing; + private Profiler.Counter counter; + + private SortableCounter(Profiler.Counter counter, boolean timing) { + this.counter = counter; + this.timing = timing; + } + + @Override + public int compareTo(SortableCounter other) { + if (timing) { + long myTime = counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED); + long otherTime = other.counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED); + return myTime < otherTime ? 1 : (myTime > otherTime ? -1 : 0); + } else { + long myInvocations = counter.getInvocations(TimeKind.INTERPRETED_AND_COMPILED); + long otherInvocations = other.counter.getInvocations(TimeKind.INTERPRETED_AND_COMPILED); + return myInvocations < otherInvocations ? 1 : (myInvocations > otherInvocations ? -1 : 0); + } + } + + } + + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RFunctionProfiler.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RFunctionProfiler.java deleted file mode 100644 index d3158a4c3e13a058b87ed179d903eaec0db6c873..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RFunctionProfiler.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.r.nodes.instrumentation; - -import java.util.Arrays; -import java.util.Map; - -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.api.vm.PolyglotEngine; -import com.oracle.truffle.r.runtime.RPerfStats; -import com.oracle.truffle.r.runtime.RSource; -import com.oracle.truffle.r.runtime.Utils; -import com.oracle.truffle.r.runtime.context.RContext; -import com.oracle.truffle.r.runtime.data.RFunction; -import com.oracle.truffle.tools.Profiler; -import com.oracle.truffle.tools.Profiler.Counter; -import com.oracle.truffle.tools.Profiler.Counter.TimeKind; - -/** - * Interface to the Truffle {@link Profiler}. - */ -public class RFunctionProfiler { - static { - RPerfStats.register(new PerfHandler()); - } - - static boolean enabled() { - return RPerfStats.enabled(PerfHandler.NAME); - } - - /** - * This is called on startup to support {@link RPerfStats}. - */ - static void installTimers(RContext context) { - if (enabled()) { - enableTiming(context, true, true); - } - } - - private static Profiler getProfiler(RContext context) { - PolyglotEngine vm = context.getVM(); - Profiler profiler = Profiler.find(vm); - return profiler; - } - - private static Profiler getProfiler() { - PolyglotEngine vm = RContext.getInstance().getVM(); - Profiler profiler = Profiler.find(vm); - return profiler; - } - - private static void enableTiming(RContext context, @SuppressWarnings("unused") boolean counting, boolean timing) { - Profiler profiler = getProfiler(context); - context.getInstrumentationState().setProfiler(profiler); - profiler.setTiming(timing); - profiler.setCollecting(true); - } - - /** - * (Interactively) installs a timer for a specific function. Currently the {@link Profiler} does - * not support profiling limited to specific functions so this effectively enables everything. - * If {@code func} is {@code null} profile all functions. In principle profiling can be - * restricted to entry counting and timing but currently counting is always on. - * - */ - public static void installTimer(@SuppressWarnings("unused") RFunction func, boolean counting, boolean timing) { - enableTiming(RContext.getInstance(), counting, timing); - } - - public static Counter getCounter(RFunction func) { - Profiler profiler = getProfiler(); - if (profiler.isCollecting()) { - String funcName = func.getTarget().getRootNode().getName(); - Map<SourceSection, Counter> counters = profiler.getCounters(); - for (Counter counter : counters.values()) { - if (counter.getName().equals(funcName)) { - return counter; - } - } - } - return null; - } - - public static void reset() { - Profiler profiler = getProfiler(); - profiler.clearData(); - profiler.setCollecting(false); - } - - public static void clear() { - Profiler profiler = getProfiler(); - profiler.clearData(); - } - - public static Counter[] getCounters() { - Profiler profiler = getProfiler(); - if (profiler.isCollecting()) { - Map<SourceSection, Counter> counters = profiler.getCounters(); - Counter[] result = new Counter[counters.size()]; - counters.values().toArray(result); - return result; - } else { - return null; - } - } - - public static boolean isTiming() { - Profiler profiler = getProfiler(); - return profiler.isTiming(); - } - - private static class PerfHandler implements RPerfStats.Handler { - static final String NAME = "timing"; - @SuppressWarnings("unused") private boolean stmts; - private int threshold; - - @Override - public void initialize(String optionText) { - if (optionText.length() > 0) { - String[] subOptions = optionText.split(":"); - for (String subOption : subOptions) { - if (subOption.equals("stmts")) { - Utils.warn("statement timing is not implemented"); - stmts = true; - } else if (subOption.startsWith("threshold")) { - threshold = Integer.parseInt(subOption.substring(subOption.indexOf('=') + 1)) * 1000; - } - } - } - } - - @Override - public String getName() { - return NAME; - } - - private static class SortableCounter implements Comparable<SortableCounter> { - private Counter counter; - - SortableCounter(Counter counter) { - this.counter = counter; - } - - @Override - public int compareTo(SortableCounter other) { - long myTime = counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED); - long otherTime = other.counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED); - return myTime < otherTime ? 1 : (myTime > otherTime ? -1 : 0); - } - - } - - /** - * Report the statement timing information at the end of the run. The report is per function - * Functions that consumed less time than requested threshold (default 0) are not included - * in the report. The report is sorted by cumulative time. - */ - @Override - public void report() { - Profiler profiler = RContext.getInstance().getInstrumentationState().getProfiler(); - // profiler.printHistograms(RPerfStats.out()); - Map<SourceSection, Counter> counters = profiler.getCounters(); - long totalTime = 0; - SortableCounter[] sortedCounters = new SortableCounter[counters.size()]; - int i = 0; - for (Counter counter : counters.values()) { - totalTime += counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED); - sortedCounters[i++] = new SortableCounter(counter); - } - Arrays.sort(sortedCounters); - for (SortableCounter scounter : sortedCounters) { - long time = scounter.counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED); - if (time > 0 && time > threshold) { - SourceSection ss = scounter.counter.getSourceSection(); - Source source = ss.getSource(); - RPerfStats.out().println("=========="); - double thisPercent = percent(time, totalTime); - RPerfStats.out().printf("%d ms (%.2f%%): %s, %s%n", time, thisPercent, scounter.counter.getName(), RSource.getOrigin(source)); - } - } - System.console(); - } - - private static double percent(long a, long b) { - return ((double) a * 100) / b; - } - - } -} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java index 25bf947a8304aebda46b083aa8fe12e93397d044..476d02bada8e22c5d41723fd882cdb0bbc2266ac 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java @@ -96,14 +96,11 @@ public class RInstrumentation { * Activate the instrumentation system for {@code context}. Currently this simply checks for the * global (command-line) options for tracing and timing. They are applied to every context. */ - public static void activate(RContext context) { + public static void activate(@SuppressWarnings("unused") RContext context) { String rdebugValue = FastROptions.Rdebug.getStringValue(); if (rdebugValue != null) { debugFunctionNames = rdebugValue.split(","); } - if (RFunctionProfiler.enabled()) { - RFunctionProfiler.installTimers(context); - } // Check for function tracing RContext.getRRuntimeASTAccess().traceAllFunctions(); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java index fb9aadcd68888f2d52dfbf0cbf6ddc9437b65fbd..0ab5761308e3f0f1be099fefdebc6dabab802672 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java @@ -43,8 +43,6 @@ public enum FastROptions { TraceCalls("Trace all R function calls", false), TraceCallsToFile("TraceCalls output is sent to 'fastr_tracecalls.log'", false), TraceNativeCalls("Trace all native function calls (performed via .Call, .External, etc.)", false), - PerfStats("PerfStats=p1,p2,...; Collect performance stats identified by p1, etc.", null, true), - PerfStatsFile("PerfStatsFile=file; Send performance stats to 'file', default stdout", null, true), Rdebug("Rdebug=f1,f2.,,,; list of R function to call debug on (implies +Instrument)", null, true), PerformanceWarnings("Print FastR performance warning", false), LoadBase("Load base package", true), diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java index 4df846f7beb1b36cfa4407146931e273abd2fd59..5069b1fd910103e0225d7f9f780375cce6cf36a3 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java @@ -11,13 +11,22 @@ */ package com.oracle.truffle.r.runtime; +import java.util.ArrayList; + import com.oracle.truffle.r.runtime.RStartParams.SA_TYPE; import com.oracle.truffle.r.runtime.context.ConsoleHandler; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.ffi.RFFIFactory; +import com.oracle.truffle.r.runtime.instrument.InstrumentationState; public class RCleanUp { + private static ArrayList<InstrumentationState.CleanupHandler> cleanupHandlers = new ArrayList<>(); + + public static void registerCleanupHandler(InstrumentationState.CleanupHandler cleanupHandler) { + cleanupHandlers.add(cleanupHandler); + } + public static void cleanUp(SA_TYPE saveType, int status, boolean runLast) { if (RInterfaceCallbacks.R_CleanUp.isOverridden()) { RFFIFactory.getRFFI().getREmbedRFFI().cleanUp(saveType.ordinal(), status, runLast ? 1 : 0); @@ -87,6 +96,14 @@ public class RCleanUp { case SUICIDE: default: + } + for (InstrumentationState.CleanupHandler cleanupHandler : cleanupHandlers) { + try { + cleanupHandler.cleanup(status); + } catch (Throwable t) { + RInternalError.reportError(t); + } + } // TODO run exit finalizers (FFI) // TODO clean tmpdir diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java deleted file mode 100644 index e655114e7aeec6d48bcc99bda719ed83909fb540..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.r.runtime; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; - -/** - * Manage the creation/activation of handlers or performance analysis. Enabled by the - * {@code FastROptions.Option.PerfStats} option. - * - * The handlers are all registered statically from the class wanting to participate. The handlers - * are enabled selectively at runtime based on the command line option. An enabled handler gets a - * call to its {@link Handler#initialize(String)} method so that it can enable its perf-mode - * behavior. - */ -public class RPerfStats { - - public interface Handler { - /** - * Called on startup if enabled to initialize any necessary state. - * - * @param optionText any text after the handler name on the command line - */ - void initialize(String optionText); - - String getName(); - - void report(); - } - - public static class Histogram { - private final long[] hist; - private int maxSize = -1; - - public Histogram(int buckets) { - hist = new long[buckets + 1]; - } - - public void inc(int size) { - if (size > maxSize) { - maxSize = size; - } - hist[effectiveBucket(size)]++; - } - - public void dec(int size) { - hist[effectiveBucket(size)]--; - } - - public int numBuckets() { - return hist.length - 1; - } - - public long getMaxSize() { - return maxSize; - } - - public int effectiveBucket(int size) { - if (size > hist.length - 1) { - return hist.length - 1; - } else { - return size; - } - } - - public long getTotalCount() { - long totalCount = 0; - for (int i = 0; i < hist.length; i++) { - totalCount += hist[i]; - } - return totalCount; - } - - public void report() { - long maxCount = -1; - for (int i = 0; i < hist.length; i++) { - if (hist[i] > maxCount) { - maxCount = hist[i]; - } - } - int fieldWidth = Long.toString(maxCount).length() + 1; - if (fieldWidth < 10) { - fieldWidth = 10; - } - String fieldWidthString = Integer.toString(fieldWidth); - String sFormat = "%-" + fieldWidthString + "s"; - String dFormat = "%-" + fieldWidthString + "d"; - out().printf(sFormat, "Size"); - for (int i = 0; i < hist.length - 1; i++) { - out().printf(dFormat, i); - } - out().printf(sFormat, "> " + (hist.length - 1)); - out().println(); - out().printf(sFormat, "Count"); - for (int i = 0; i < hist.length - 1; i++) { - out().printf(dFormat, hist[i]); - } - out().printf(dFormat, hist[hist.length - 1]); - out().println(); - } - } - - private static final ArrayList<Handler> handlers = new ArrayList<>(); - private static boolean initialized; - private static PrintStream out = System.out; - - public static PrintStream out() { - return out; - } - - /** - * Register a {@link Handler}. This should be done in a {@code static} block so that, in an AOT - * VM, all handlers are included in the image. N.B. Owing to dynamic class loading in a standard - * VM, this may be called after {@link RPerfStats#initialize}, so we may have to invoke - * {@code handler.initialize} from here. - */ - public static void register(Handler handler) { - handlers.add(handler); - if (initialized) { - String optionText = getOptionText(handler.getName()); - if (optionText != null) { - handler.initialize(optionText); - } - } - } - - /** - * Called by the engine startup sequence to initialize all registered handlers. - */ - public static void initialize() { - for (Handler handler : handlers) { - String optionText = getOptionText(handler.getName()); - if (optionText != null) { - handler.initialize(optionText); - } - } - initialized = true; - } - - private static String getOptionText(String name) { - String optionValue = getPerfStatsOption(name); - if (optionValue != null) { - if (optionValue.length() > 0) { - optionValue = optionValue.substring(name.length()); - } - } - return optionValue; - } - - private static String getPerfStatsOption(String name) { - return FastROptions.matchesElement(name, FastROptions.PerfStats.getStringValue()); - } - - public static boolean enabled(String name) { - return getPerfStatsOption(name) != null; - } - - private static boolean reporting; - - /** - * Called just before FastR exits. - */ - public static void report() { - if (reporting) { - // some crash in a reporter caused a recursive entry - return; - } - reporting = true; - String file = FastROptions.PerfStatsFile.getStringValue(); - if (file != null) { - try { - out = new PrintStream(new FileOutputStream(file)); - } catch (IOException ex) { - System.err.print("PerfStats: can't open " + file + " for output, using stdout"); - } - } - boolean headerOutput = false; - for (Handler handler : handlers) { - if (enabled(handler.getName())) { - if (!headerOutput) { - out().println("RPerfStats Reports"); - headerOutput = true; - } - handler.report(); - } - } - } -} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java index 486c4b98687d8b2f54e026a3d432d2ecdb9cd7c0..35ed26d1d5d52539a19cbc667e585d10b4a8b45a 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java @@ -173,11 +173,8 @@ public final class Utils { * This the real, final, non-overrideable, exit of the entire R system. TODO well, modulo how * quit() is interpreted when R is started implicitly from a Polyglot shell that is running * other languages. - * - * @param status */ public static void systemExit(int status) { - RPerfStats.report(); System.exit(status); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributes.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributes.java index 69956311d4b3aa3156ec465a70e3fa9fceb015b0..328b2cc713a467155b4e3e5fd46f2e2022d9e50f 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributes.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributes.java @@ -23,15 +23,16 @@ package com.oracle.truffle.r.runtime.data; import java.util.Arrays; +import java.util.Deque; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentLinkedDeque; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerDirectives.ValueType; -import com.oracle.truffle.api.profiles.ConditionProfile; -import com.oracle.truffle.r.runtime.RPerfStats; +import com.oracle.truffle.api.utilities.CyclicAssumption; /** * Provides the generic mechanism for associating attributes with a R object. It does no special @@ -71,8 +72,6 @@ public final class RAttributes implements Iterable<RAttributes.RAttribute> { } } - private static final ConditionProfile statsProfile = ConditionProfile.createBinaryProfile(); - public static RAttributes create() { return new RAttributes(); } @@ -98,8 +97,8 @@ public final class RAttributes implements Iterable<RAttributes.RAttribute> { */ RAttributes() { - if (statsProfile.profile(stats != null)) { - stats.init(); + if (AttributeTracer.enabled) { + AttributeTracer.reportAttributeChange(AttributeTracer.Change.CREATE, this, null); } } @@ -130,6 +129,7 @@ public final class RAttributes implements Iterable<RAttributes.RAttribute> { public void put(String name, Object value) { assert isInterned(name); int pos = find(name); + int spos = pos; if (pos == -1) { ensureFreeSpace(); pos = size++; @@ -139,8 +139,8 @@ public final class RAttributes implements Iterable<RAttributes.RAttribute> { // assert value == null || !(value instanceof RShareable) || !((RShareable) // value).isTemporary(); values[pos] = value; - if (statsProfile.profile(stats != null)) { - stats.update(this); + if (AttributeTracer.enabled) { + AttributeTracer.reportAttributeChange(spos == -1 ? AttributeTracer.Change.ADD : AttributeTracer.Change.UPDATE, this, name); } } @@ -149,6 +149,9 @@ public final class RAttributes implements Iterable<RAttributes.RAttribute> { names = Arrays.copyOf(names, (size + 1) * 2); values = Arrays.copyOf(values, (size + 1) * 2); assert names.length == values.length; + if (AttributeTracer.enabled) { + AttributeTracer.reportAttributeChange(AttributeTracer.Change.GROW, this, names.length); + } } } @@ -202,6 +205,9 @@ public final class RAttributes implements Iterable<RAttributes.RAttribute> { } names[size] = null; values[size] = null; + if (AttributeTracer.enabled) { + AttributeTracer.reportAttributeChange(AttributeTracer.Change.REMOVE, this, name); + } } } @@ -267,55 +273,64 @@ public final class RAttributes implements Iterable<RAttributes.RAttribute> { throw new NoSuchElementException(); } - // Performance analysis - - @CompilationFinal private static PerfHandler stats; - - static { - RPerfStats.register(new PerfHandler()); + public void setSize(int size) { + this.size = size; } - /** - * Collects data on the maximum size of the attribute set. So only interested in adding not - * removing attributes. - */ - private static class PerfHandler implements RPerfStats.Handler { - private static final RPerfStats.Histogram hist = new RPerfStats.Histogram(5); + public static final class AttributeTracer { + private static Deque<Listener> listeners = new ConcurrentLinkedDeque<>(); + @CompilationFinal private static boolean enabled; - @TruffleBoundary - void init() { - hist.inc(0); + private static final CyclicAssumption noAttributeTracingAssumption = new CyclicAssumption("data copying"); + + private AttributeTracer() { + // only static methods } - @TruffleBoundary - void update(RAttributes attr) { - // incremented size by 1 - int s = attr.size(); - int effectivePrevSize = hist.effectiveBucket(s - 1); - int effectiveSizeNow = hist.effectiveBucket(s); - hist.dec(effectivePrevSize); - hist.inc(effectiveSizeNow); + /** + * Adds a listener of attribute events. + */ + public static void addListener(Listener listener) { + listeners.addLast(listener); } - @Override - public void initialize(String optionText) { - stats = this; + /** + * After calling this method attribute related events will be reported to the listener. This + * invalidates global assumption and should be used with caution. + */ + public static void setTracingState(boolean newState) { + if (enabled != newState) { + noAttributeTracingAssumption.invalidate(); + enabled = newState; + } } - @Override - public String getName() { - return "attributes"; + public enum Change { + CREATE, + ADD, + UPDATE, + REMOVE, + GROW + } + public static void reportAttributeChange(Change change, RAttributes attrs, Object value) { + if (enabled) { + for (Listener listener : listeners) { + listener.reportAttributeChange(change, attrs, value); + } + } } - @Override - public void report() { - RPerfStats.out().printf("RAttributes: %d, max size %d%n", hist.getTotalCount(), hist.getMaxSize()); - hist.report(); + public interface Listener { + /** + * Reports attribute events to the listener. If there are no traced objects, this should + * turn into no-op. {@code valuer} depends on the value of {@code change}. For + * {@code CREATE} it is {@code null}, for {@code ADD,UPDATE, REMOVE} it is the + * {@code String} name of the attribute and for {@code GROW} it is the (new) size. + */ + void reportAttributeChange(Change change, RAttributes attrs, Object value); } - } - public void setSize(int size) { - this.size = size; } + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java index dad4afd45a6e0e52823f4e946f459dd9a5514e1e..b9c726e6fcc73b1770b32b45eb6808b099a63c3f 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java @@ -24,27 +24,22 @@ package com.oracle.truffle.r.runtime.data; import java.util.Arrays; import java.util.Deque; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.atomic.AtomicInteger; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.utilities.CyclicAssumption; import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.RInternalError; -import com.oracle.truffle.r.runtime.RPerfStats; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.data.RPromise.Closure; import com.oracle.truffle.r.runtime.data.RPromise.EagerFeedback; import com.oracle.truffle.r.runtime.data.RPromise.PromiseState; -import com.oracle.truffle.r.runtime.data.model.RAbstractVector; import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.gnur.SEXPTYPE; import com.oracle.truffle.r.runtime.nodes.RNode; @@ -505,7 +500,7 @@ public final class RDataFactory { @CompilationFinal private static boolean enabled; private static final CyclicAssumption noAllocationTracingAssumption = new CyclicAssumption("data allocation"); - public static void setAllocationTracing(boolean newState) { + public static void setTracingState(boolean newState) { if (enabled != newState) { noAllocationTracingAssumption.invalidate(); enabled = newState; @@ -541,64 +536,4 @@ public final class RDataFactory { listeners.addLast(listener); } - /* - * (Legacy) support for R:PerfStats option. This does produce more information than Rprofmem - * regarding the types and length of the objects being allocated, but it does not record where - * in R the allocation took place. - */ - static { - RPerfStats.register(new PerfHandler()); - } - - private static class PerfHandler implements RPerfStats.Handler, Listener { - private static Map<Class<?>, RPerfStats.Histogram> histMap; - - @Override - public void initialize(String optionData) { - histMap = new HashMap<>(); - addListener(this); - setAllocationTracing(true); - } - - @Override - public String getName() { - return "datafactory"; - } - - @Override - @TruffleBoundary - public void reportAllocation(RTypedValue data) { - Class<?> klass = data.getClass(); - boolean isBounded = data instanceof RAbstractVector; - RPerfStats.Histogram hist = histMap.get(klass); - if (hist == null) { - hist = new RPerfStats.Histogram(isBounded ? 10 : 1); - histMap.put(klass, hist); - } - int length = isBounded ? ((RAbstractVector) data).getLength() : 0; - hist.inc(length); - } - - @Override - public void report() { - RPerfStats.out().println("Scalar types"); - for (Map.Entry<Class<?>, RPerfStats.Histogram> entry : histMap.entrySet()) { - RPerfStats.Histogram hist = entry.getValue(); - if (hist.numBuckets() == 1) { - RPerfStats.out().printf("%s: %d%n", entry.getKey().getSimpleName(), hist.getTotalCount()); - } - } - RPerfStats.out().println(); - RPerfStats.out().println("Vector types"); - for (Map.Entry<Class<?>, RPerfStats.Histogram> entry : histMap.entrySet()) { - RPerfStats.Histogram hist = entry.getValue(); - if (hist.numBuckets() > 1) { - RPerfStats.out().printf("%s: %d, max size %d%n", entry.getKey().getSimpleName(), hist.getTotalCount(), hist.getMaxSize()); - entry.getValue().report(); - } - } - RPerfStats.out().println(); - } - - } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java index a75b54fdb4bd80a721e5dfb173f611368db7cb62..0115ab8555136eec2d0fc9d786b4a4aa9726b49a 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java @@ -26,12 +26,9 @@ import java.util.function.Function; import com.oracle.truffle.api.CompilerAsserts; 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.profiles.ConditionProfile; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalError; -import com.oracle.truffle.r.runtime.RPerfStats; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.SuppressFBWarnings; @@ -553,7 +550,6 @@ public abstract class RVector extends RSharingAttributeStorage implements RShare public final RVector copy() { RVector result = internalCopyAndReport(); setAttributes(result); - incCopyCount(); result.setTypedValueInfo(getTypedValueInfo()); return result; } @@ -842,45 +838,6 @@ public abstract class RVector extends RSharingAttributeStorage implements RShare } } - private static final ConditionProfile statsProfile = ConditionProfile.createBinaryProfile(); - - @CompilationFinal private static PerfHandler stats; - - private static void incCopyCount() { - if (statsProfile.profile(stats != null)) { - stats.record(null); - } - } - - static { - RPerfStats.register(new PerfHandler()); - } - - private static class PerfHandler implements RPerfStats.Handler { - - private static int count; - - void record(@SuppressWarnings("unused") Object data) { - count++; - } - - @Override - public void initialize(String optionData) { - stats = this; - count = 0; - } - - @Override - public String getName() { - return "vectorcopies"; - } - - @Override - public void report() { - RPerfStats.out().printf("NUMBER OF VECTOR COPIES: %d\n", count); - } - } - private static final int MAX_TOSTRING_LENGTH = 100; protected String toString(Function<Integer, String> element) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java index 0b601ce31970ad98c9347a687fc8de366dbcef6c..786cd82929516d3c34d38258ac1554978a7e6416 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java @@ -22,21 +22,22 @@ */ package com.oracle.truffle.r.runtime.instrument; -import java.io.PrintWriter; +import java.io.PrintStream; import java.util.HashSet; +import java.util.Map; import java.util.WeakHashMap; - +import java.util.concurrent.ConcurrentHashMap; import com.oracle.truffle.api.instrumentation.EventBinding; import com.oracle.truffle.api.instrumentation.ExecutionEventListener; import com.oracle.truffle.api.instrumentation.Instrumenter; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.vm.PolyglotEngine; +import com.oracle.truffle.r.runtime.RCleanUp; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.tools.Profiler; /** - * The tracingState is a global variable in R, so we store it (and the associated listener objects) - * in the {@link RContext}. We also store related {@code debug} state, as that is also context - * specific. + * Collects together all the context-specific state related to profiling, instrumentation. * */ public final class InstrumentationState implements RContext.ContextState { @@ -63,17 +64,9 @@ public final class InstrumentationState implements RContext.ContextState { */ private Profiler profiler; - /** - * The {@link RprofState} state, if any, associated with this {@link RContext}. - */ - private final RprofState rprofState; - - /** - * The {@link RprofmemState} state, if any, associated with this {@link RContext}. - */ - private final RprofmemState rprofmemState; + private TracememContext tracememContext; - private final TracememContext tracememContext; + Map<String, RprofState> rprofStates = new ConcurrentHashMap<>(7); /** * State used by the {@code tracemem} built-in. @@ -92,7 +85,7 @@ public final class InstrumentationState implements RContext.ContextState { /** * The {@link BrowserState} state, if any, associated with this {@link RContext}. */ - private final BrowserState browserState; + private BrowserState browserState; /** * Whether debugging is globally disabled in this {@link RContext}. Used to (temporarily) @@ -101,107 +94,28 @@ public final class InstrumentationState implements RContext.ContextState { */ private boolean debugGloballyDisabled; - private abstract static class RprofAdapter { - protected PrintWriter out; + public abstract static class RprofState implements CleanupHandler { + private PrintStream out; + + protected RprofState() { + RCleanUp.registerCleanupHandler(this); + } /** * Return current output or {@code null} if not profiling. */ - public PrintWriter out() { + public PrintStream out() { return out; } - public void setOut(PrintWriter out) { + public void setOut(PrintStream out) { this.out = out; } - } - - /** - * State used by {@code Rprof}. - * - */ - public static final class RprofState extends RprofAdapter { - private Thread profileThread; - private ExecutionEventListener statementListener; - private long intervalInMillis; - private boolean lineProfiling; - private MemoryQuad memoryQuad; - - public static final class MemoryQuad { - public long smallV; - public long largeV; - public long nodes; - public long copied; - - public MemoryQuad copyAndClear() { - MemoryQuad result = new MemoryQuad(); - result.copied = copied; - result.largeV = largeV; - result.smallV = smallV; - result.nodes = nodes; - copied = 0; - largeV = 0; - smallV = 0; - nodes = 0; - return result; - } - } - - public void initialize(PrintWriter outA, Thread profileThreadA, ExecutionEventListener statementListenerA, long intervalInMillisA, - boolean lineProfilingA, boolean memoryProfilingA) { - this.out = outA; - this.profileThread = profileThreadA; - this.statementListener = statementListenerA; - this.intervalInMillis = intervalInMillisA; - this.lineProfiling = lineProfilingA; - this.memoryQuad = memoryProfilingA ? new MemoryQuad() : null; - } - - public boolean lineProfiling() { - return lineProfiling; - } - - public boolean memoryProfiling() { - return memoryQuad != null; - } - - public MemoryQuad memoryQuad() { - return memoryQuad; - } - - public long intervalInMillis() { - return intervalInMillis; - } - - public ExecutionEventListener statementListener() { - return statementListener; - } - - public Thread profileThread() { - return profileThread; - } - - } - - public static final class RprofmemState extends RprofAdapter { - private double threshold; - private int pageCount; - - public void initialize(PrintWriter outA, double thresholdA) { - this.out = outA; - this.threshold = thresholdA; - } - - public double threshold() { - return threshold; - } - public int pageCount() { - return pageCount; - } - - public void setPageCount(int pageCount) { - this.pageCount = pageCount; + public void closeAndResetOut() { + out.flush(); + out.close(); + out = null; } } @@ -226,12 +140,17 @@ public final class InstrumentationState implements RContext.ContextState { } } + /** + * An interface that can be used to register an action to be taken when the system shuts down as + * part of the {@code R_Cleanup}. + * + */ + public interface CleanupHandler { + void cleanup(int status); + } + private InstrumentationState(Instrumenter instrumenter) { this.instrumenter = instrumenter; - this.rprofState = new RprofState(); - this.rprofmemState = new RprofmemState(); - this.tracememContext = new TracememContext(); - this.browserState = new BrowserState(); } public void putTraceBinding(SourceSection ss, EventBinding<?> binding) { @@ -267,11 +186,11 @@ public final class InstrumentationState implements RContext.ContextState { return tracingState; } - public void setProfiler(Profiler profiler) { - this.profiler = profiler; - } - public Profiler getProfiler() { + if (profiler == null) { + PolyglotEngine vm = RContext.getInstance().getVM(); + profiler = Profiler.find(vm); + } return profiler; } @@ -279,19 +198,26 @@ public final class InstrumentationState implements RContext.ContextState { return instrumenter; } - public RprofState getRprof() { - return rprofState; + public RprofState getRprofState(String name) { + RprofState state = rprofStates.get(name); + return state; } - public RprofmemState getRprofmem() { - return rprofmemState; + public void setRprofState(String name, RprofState state) { + rprofStates.put(name, state); } public TracememContext getTracemem() { + if (tracememContext == null) { + tracememContext = new TracememContext(); + } return tracememContext; } public BrowserState getBrowserState() { + if (browserState == null) { + browserState = new BrowserState(); + } return browserState; } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java index 7b40db76e668e950a4a887f015dafb093c931042..189d8ab31ee6be1b3561b5f67e0565bb191f590f 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java @@ -38,7 +38,6 @@ import org.junit.runner.Description; import org.junit.runner.Result; import com.oracle.truffle.r.runtime.RInternalError; -import com.oracle.truffle.r.runtime.RPerfStats; import com.oracle.truffle.r.runtime.ResourceHandlerFactory; import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.context.ContextInfo; @@ -203,7 +202,6 @@ public class TestBase { if (diffsOutputFile != null) { TestOutputManager.writeDiffsTestOutputFile(diffsOutputFile, expectedOutputManager, fastROutputManager); } - RPerfStats.report(); } catch (IOException ex) { throw new RuntimeException(ex); }