diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java index 30a134642b932dbd8197084dfad5f5562ecdf335..04c66c9f1e98f0a38d1966e699a4142dfe2392ad 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java @@ -242,7 +242,7 @@ public class RCommand { } else if (cause instanceof ExitException) { // usually from quit vm.dispose(); - System.exit(((ExitException) cause).getStatus()); + Utils.systemExit(((ExitException) cause).getStatus()); } else { RInternalError.reportErrorAndConsoleLog(cause, consoleHandler, 0); // We continue the repl even though the system may be broken @@ -262,7 +262,7 @@ public class RCommand { } catch (Throwable e) { if (e.getCause() instanceof ExitException) { // normal quit, but with exit code based on lastStatus - System.exit(lastStatus); + Utils.systemExit(lastStatus); } throw RInternalError.shouldNotReachHere(e); } diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java index 5b4d80c96f3f3d8b085cf7f2a706ebb056a7eba9..b952a0da7a3b74f43725f163a7221b7d57d48379 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java @@ -22,199 +22,56 @@ */ package com.oracle.truffle.r.library.utils; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.r.library.utils.ObjectSizeNodeGen.RecursiveObjectSizeNodeGen; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode; -import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode; -import com.oracle.truffle.r.runtime.data.RAttributable; -import com.oracle.truffle.r.runtime.data.RAttributes; -import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute; -import com.oracle.truffle.r.runtime.data.RComplex; -import com.oracle.truffle.r.runtime.data.RComplexVector; -import com.oracle.truffle.r.runtime.data.RDoubleSequence; -import com.oracle.truffle.r.runtime.data.RDoubleVector; -import com.oracle.truffle.r.runtime.data.RExpression; +import com.oracle.truffle.r.runtime.data.RObjectSize; import com.oracle.truffle.r.runtime.data.RFunction; -import com.oracle.truffle.r.runtime.data.RIntSequence; -import com.oracle.truffle.r.runtime.data.RIntVector; -import com.oracle.truffle.r.runtime.data.RLanguage; -import com.oracle.truffle.r.runtime.data.RList; -import com.oracle.truffle.r.runtime.data.RLogicalVector; import com.oracle.truffle.r.runtime.data.RNull; -import com.oracle.truffle.r.runtime.data.RPairList; -import com.oracle.truffle.r.runtime.data.RRaw; -import com.oracle.truffle.r.runtime.data.RRawVector; -import com.oracle.truffle.r.runtime.data.RStringVector; +import com.oracle.truffle.r.runtime.data.RTypedValue; -/* - * Similarly to GNU R's version, this is very approximate - * (e.g. overhead related to Java object headers is not included) - * and is only (semi) accurate for atomic vectors. +/** + * Similarly to GNU R's version, this is approximate and based, for {@link RTypedValue} instances on + * {@link RObjectSize#getObjectSize}. As per GNU R the AST size for a closure is included. TODO AST + * size not included owing to problems sizing it automatically. */ +@SuppressWarnings("unused") public abstract class ObjectSize extends RExternalBuiltinNode.Arg1 { - protected abstract int executeInt(Object o); - - @Child RecursiveObjectSize recursiveObjectSize; - - protected int recursiveObjectSize(Object o) { - if (recursiveObjectSize == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - recursiveObjectSize = insert(RecursiveObjectSizeNodeGen.create()); - } - return recursiveObjectSize.executeInt(o); - } - - @Specialization - protected int objectSize(@SuppressWarnings("unused") RNull o) { - return 64; // pointer? - } - - @Specialization - protected int objectSize(@SuppressWarnings("unused") int o) { - return 32; - } - - @Specialization - protected int objectSize(@SuppressWarnings("unused") double o) { - return 64; - } - - @Specialization - protected int objectSize(@SuppressWarnings("unused") byte o) { - return 8; - } - - @Specialization - protected int objectSize(String o) { - return o.length() * 16; - } - - @Specialization - protected int objectSize(@SuppressWarnings("unused") RRaw o) { - return 8; - } - - @Specialization - protected int objectSize(@SuppressWarnings("unused") RComplex o) { - return 128; - } - - @Specialization - protected int objectSize(RIntSequence o) { - int res = 96; // int length + int start + int stride - return res + attrSize(o); - } - - @Specialization - protected int objectSize(RDoubleSequence o) { - int res = 160; // int length + double start + double stride - return res + attrSize(o); - } - - @Specialization - protected int objectSize(RIntVector o) { - return o.getLength() * 32 + attrSize(o); - } - - @Specialization - protected int objectSize(RDoubleVector o) { - return o.getLength() * 64 + attrSize(o); - } - - @Specialization - protected int objectSize(RStringVector o) { - int res = 0; - for (int i = 0; i < o.getLength(); i++) { - res += o.getLength() * 16; - } - return res + attrSize(o); - } - - @Specialization - protected int objectSize(RLogicalVector o) { - return o.getLength() * 8 + attrSize(o); - } - - @Specialization - protected int objectSize(RComplexVector o) { - return o.getLength() * 128 + attrSize(o); - } - - @Specialization - protected int objectSize(RRawVector o) { - return o.getLength() * 8 + attrSize(o); - } - - @Specialization - @TruffleBoundary - protected int objectSize(RList o) { - int res = 0; - for (int i = 0; i < o.getLength(); i++) { - res += recursiveObjectSize(o.getDataAt(i)); - } - return res + attrSize(o); - } - - @Specialization - @TruffleBoundary - protected int objectSize(RPairList o) { - RPairList list = o; - Object car = list.car(); - int res = 0; - while (true) { - res += recursiveObjectSize(car); - Object cdr = list.cdr(); - if (cdr == RNull.instance) { - break; + private static class MyIgnoreObjectHandler implements RObjectSize.IgnoreObjectHandler { + @Override + public boolean ignore(Object rootObject, Object obj) { + if (obj == RNull.instance) { + return true; } else { - list = (RPairList) cdr; - car = list.car(); + return false; } } - return res + attrSize(o); - } - @Specialization - protected int objectSize(@SuppressWarnings("unused") RFunction o) { - return 256; // arbitrary, but does it really matter? } + private static final MyIgnoreObjectHandler ignoreObjectHandler = new MyIgnoreObjectHandler(); + @Specialization - protected int objectSize(@SuppressWarnings("unused") RLanguage o) { - return 256; // arbitrary, but does it really matter? + protected int objectSize(int o) { + return RObjectSize.INT_SIZE; } @Specialization - protected int objectSize(@SuppressWarnings("unused") RExpression o) { - return 256; // arbitrary, but does it really matter? + protected int objectSize(double o) { + return RObjectSize.DOUBLE_SIZE; } - protected int attrSize(RAttributable o) { - return o.getAttributes() == null ? 0 : attrSizeInternal(o.getAttributes()); + @Specialization + protected int objectSize(byte o) { + return RObjectSize.BYTE_SIZE; } + @Fallback @TruffleBoundary - protected int attrSizeInternal(RAttributes attributes) { - int size = 0; - for (RAttribute attr : attributes) { - size += attr.getName().length() * 16; - size += recursiveObjectSize(attr.getValue()); - } - return size; - } - - protected abstract static class RecursiveObjectSize extends TruffleBoundaryNode { - - protected abstract int executeInt(Object o); - - @Child ObjectSize objectSize = ObjectSizeNodeGen.create(); - - @Specialization - protected int objectSize(Object o) { - return objectSize.executeInt(o); - } + protected int objectSize(Object o) { + return (int) RObjectSize.getObjectSize(o, ignoreObjectHandler); } } 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 d03f663b1151eaae9a39bdecede4f62e9996a80e..db38227316cc29e6eec6064ae342363e887c6018 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 @@ -50,12 +50,36 @@ import com.oracle.truffle.r.runtime.RRuntime; 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.RDataFactory; import com.oracle.truffle.r.runtime.data.RNull; +import com.oracle.truffle.r.runtime.data.RObjectSize; +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.nodes.RSyntaxNode; -public abstract class Rprof extends RExternalBuiltinNode.Arg8 { +/** + * Implements the {@code Rprof} external. + * + * The output is basically a sequence of call stacks, output at each sample interval, with entries + * in the stack identified by quoted function names. If memory profiling, the stack is preceded by a + * auad of numbers {@code :smallv:bigv:nodes:duplicate_counter:} allocated in the interval. If line + * profiling is enabled source files are listed as + * + * <pre> + * #File N: path + * </pre> + * + * and then the {@code N} is used in line number references of the form {@code N#L},which precede + * the function name. + * + */ +public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFactory.Listener, MemoryCopyTracer.Listener { + + private RprofState profState; @SuppressWarnings("unused") @Specialization @@ -64,13 +88,13 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 { if (!RContext.getInstance().isInitial()) { throw RError.error(this, RError.Message.GENERIC, "profiling not supported in created contexts"); } - RprofState profState = RContext.getInstance().stateInstrumentation.getRprof(); + profState = RContext.getInstance().stateInstrumentation.getRprof(); String filename = filenameVec.getDataAt(0); if (filename.length() == 0) { // disable endProfiling(); } else { - // enable + // enable after ending any previous session if (profState.out() != null) { endProfiling(); } @@ -79,18 +103,21 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 { boolean gcProfiling = RRuntime.fromLogical(gcProfilingL); try { PrintWriter out = new PrintWriter(new FileWriter(filename, append)); - if (memProfiling) { - RError.warning(this, RError.Message.GENERIC, "Rprof: memory profiling not supported"); - } if (gcProfiling) { RError.warning(this, RError.Message.GENERIC, "Rprof: gc profiling not supported"); } + if (memProfiling) { + RDataFactory.addListener(this); + RDataFactory.setAllocationTracing(true); + MemoryCopyTracer.addListener(this); + MemoryCopyTracer.setTracingState(true); + } // interval is in seconds, we convert to millis long intervalInMillis = (long) (1E3 * intervalD); StatementListener statementListener = new StatementListener(); ProfileThread profileThread = new ProfileThread(intervalInMillis, statementListener); profileThread.setDaemon(true); - profState.initialize(out, profileThread, statementListener, intervalInMillis, RRuntime.fromLogical(lineProfilingL)); + profState.initialize(out, profileThread, statementListener, intervalInMillis, RRuntime.fromLogical(lineProfilingL), memProfiling); profileThread.start(); } catch (IOException ex) { throw RError.error(this, RError.Message.GENERIC, String.format("Rprof: cannot open profile file '%s'", filename)); @@ -99,13 +126,37 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 { return RNull.instance; } - private static void endProfiling() { - RprofState profState = RContext.getInstance().stateInstrumentation.getRprof(); + @Override + @TruffleBoundary + public void reportAllocation(RTypedValue data) { + long size = RObjectSize.getObjectSize(data, Rprofmem.myIgnoreObjectHandler); + if (data instanceof RAbstractVector) { + if (size >= Rprofmem.LARGE_VECTOR) { + profState.memoryQuad().largeV += size; + } else { + profState.memoryQuad().smallV += size; + } + } else { + profState.memoryQuad().nodes += size; + } + + } + + @Override + @TruffleBoundary + public void reportCopying(RAbstractVector source, RAbstractVector dest) { + 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: "); + } if (profState.lineProfiling()) { out.print("line profiling: "); } @@ -124,7 +175,12 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 { } } } + 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) { @@ -139,8 +195,15 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 { } } out.println(); + index++; } out.close(); + profState.setOut(null); + if (profState.memoryProfiling()) { + RDataFactory.setAllocationTracing(false); + MemoryCopyTracer.setTracingState(false); + } + } private static String getPath(RSyntaxNode node) { @@ -177,8 +240,9 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 { * Emulates a sampling timer by checking when the sample interval rolls over and at that point * collects the stack of functions. */ - private static final class StatementListener implements ExecutionEventListener { + private final class StatementListener implements ExecutionEventListener { private ArrayList<ArrayList<RSyntaxNode>> intervalStacks = new ArrayList<>(); + private ArrayList<MemoryQuad> intervalMemory = new ArrayList<>(); private volatile boolean newInterval; private StatementListener() { @@ -200,12 +264,16 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 { stack.add((RSyntaxNode) context.getInstrumentedNode()); collectStack(stack); intervalStacks.add(stack); + if (profState.memoryProfiling()) { + intervalMemory.add(profState.memoryQuad().copyAndClear()); + } + newInterval = false; } } @TruffleBoundary - private static void collectStack(final ArrayList<RSyntaxNode> stack) { + private void collectStack(final ArrayList<RSyntaxNode> stack) { Utils.iterateRFrames(FrameAccess.READ_ONLY, new Function<Frame, Object>() { @Override 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 new file mode 100644 index 0000000000000000000000000000000000000000..5fa5c672e50bcf65325c39085cb018110c0dc822 --- /dev/null +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java @@ -0,0 +1,149 @@ +/* + * 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.library.utils; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode; +import com.oracle.truffle.r.runtime.RArguments; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.Utils; +import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.data.RNull; +import com.oracle.truffle.r.runtime.data.RTypedValue; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RObjectSize; +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; + +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) { + endProfiling(); + } + boolean append = RRuntime.fromLogical(appendL); + try { + PrintWriter out = new PrintWriter(new FileWriter(filename, append)); + profmemState.initialize(out, thresholdVec.getDataAt(0)); + RDataFactory.addListener(this); + RDataFactory.setAllocationTracing(true); + } catch (IOException ex) { + throw RError.error(this, RError.Message.GENERIC, String.format("Rprofmem: cannot open profile file '%s'", filename)); + } + } + return RNull.instance; + } + + private void endProfiling() { + if (profmemState != null) { + RDataFactory.setAllocationTracing(false); + profmemState.out().flush(); + profmemState.out().close(); + profmemState.setOut(null); + } + } + + private static final int PAGE_SIZE = 2000; + static final int LARGE_VECTOR = 128; + + /** + * We ignore nested {@link RTypedValue} instances as these will have been counted already. We + * also ignore {@link Node} instances, except in {@link RFunction} objects. + */ + private static class MyIgnoreObjectHandler implements RObjectSize.IgnoreObjectHandler { + @Override + public boolean ignore(Object rootObject, Object obj) { + if (obj == RNull.instance) { + return true; + } else { + Class<?> klass = obj.getClass(); + if (RTypedValue.class.isAssignableFrom(klass)) { + return true; + } else { + return false; + } + } + } + + } + + static final RObjectSize.IgnoreObjectHandler myIgnoreObjectHandler = new MyIgnoreObjectHandler(); + + @Override + @TruffleBoundary + public void reportAllocation(RTypedValue data) { + // We could do some in memory buffering + // TODO write out full stack + Frame frame = Utils.getActualCurrentFrame(); + if (frame == null) { + // not an R evaluation, some internal use + return; + } + RFunction func = RArguments.getFunction(frame); + if (func == null) { + return; + } + String name = func.getRootNode().getName(); + + long size = RObjectSize.getObjectSize(data, myIgnoreObjectHandler); + if (data instanceof RAbstractVector && size >= LARGE_VECTOR) { + profmemState.out().printf("%d: %s\n", size, name); + } else { + 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)); + } else { + profmemState.setPageCount((int) pcs); + } + } + + } + +} diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java index d294b653a4db4d9fc01226325f0dca790b9db417..13c44d60cfdd6e5a75207c2b9d794f8507b106a1 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java @@ -54,7 +54,7 @@ import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.conn.StdConnections; import com.oracle.truffle.r.runtime.context.RContext; -import com.oracle.truffle.r.runtime.data.MemoryTracer; +import com.oracle.truffle.r.runtime.data.MemoryCopyTracer; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RNull; @@ -79,7 +79,7 @@ public class TraceFunctions { public abstract static class PrimTrace extends Helper { @Specialization - protected RNull primUnTrace(VirtualFrame frame, RAbstractStringVector funcName) { + protected RNull primTrace(VirtualFrame frame, RAbstractStringVector funcName) { return primTrace((RFunction) getFunction(frame, funcName.getDataAt(0))); } @@ -118,10 +118,12 @@ public class TraceFunctions { @Specialization @TruffleBoundary protected byte traceOnOff(byte state) { + /* TODO GnuR appears to accept ANY value as an argument */ boolean prevState = RContext.getInstance().stateInstrumentation.getTracingState(); boolean newState = RRuntime.fromLogical(state); if (newState != prevState) { RContext.getInstance().stateInstrumentation.setTracingState(newState); + MemoryCopyTracer.setTracingState(newState); } return RRuntime.asLogical(prevState); } @@ -135,7 +137,7 @@ public class TraceFunctions { public abstract static class TracememBase extends RBuiltinNode { static { - MemoryTracer.setListener(new TracememBase.TracememListener()); + MemoryCopyTracer.addListener(new TracememBase.TracememListener()); } protected static HashSet<Object> getTracedObjects() { @@ -147,8 +149,16 @@ public class TraceFunctions { } protected static void startTracing(Object x) { + /* + * There is no explicit command to enable tracing, it is implicit in the call to + * tracemem. However, it can be disabled by tracingState(F), so we can't unilaterally + * turn on tracing here. + */ getTracedObjects().add(x); - MemoryTracer.reportEvents(); + boolean tracingState = RContext.getInstance().stateInstrumentation.getTracingState(); + if (tracingState) { + MemoryCopyTracer.setTracingState(true); + } } protected static void printToStdout(String msg) { @@ -176,7 +186,7 @@ public class TraceFunctions { return result.toString(); } - private static final class TracememListener implements MemoryTracer.Listener { + private static final class TracememListener implements MemoryCopyTracer.Listener { @Override public void reportCopying(RAbstractVector src, RAbstractVector dest) { if (getTracedObjects().contains(src)) { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java index 582337c61d1c2c1eff10889bcbff2597c3761af0..624b0baa010ced6e4ec76b6d3e99758a2bff94c1 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java @@ -66,6 +66,7 @@ import com.oracle.truffle.r.library.utils.Download; import com.oracle.truffle.r.library.utils.MenuNodeGen; import com.oracle.truffle.r.library.utils.ObjectSizeNodeGen; import com.oracle.truffle.r.library.utils.RprofNodeGen; +import com.oracle.truffle.r.library.utils.RprofmemNodeGen; import com.oracle.truffle.r.library.utils.TypeConvertNodeGen; import com.oracle.truffle.r.library.utils.WriteTable; import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode; @@ -576,8 +577,9 @@ public class ForeignFunctions { return getExternalModelBuiltinNode("termsform"); case "Rprof": return RprofNodeGen.create(); - case "unzip": case "Rprofmem": + return RprofmemNodeGen.create(); + case "unzip": case "addhistory": case "loadhistory": case "savehistory": 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 8733ab397016e835599532c18d617f86ab877e7b..486c4b98687d8b2f54e026a3d432d2ecdb9cd7c0 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 @@ -169,6 +169,18 @@ public final class Utils { throw new ExitException(2); } + /** + * 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); + } + private static String userHome; private static String userHome() { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..7e303054edb4f818d2153f011bac4e37a0baebff --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java @@ -0,0 +1,228 @@ +/* + * 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.runtime.data; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import javax.tools.ToolProvider; + +import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.Utils; +import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler; +import com.oracle.truffle.r.runtime.data.RObjectSize.TypeCustomizer; + +/** + * Uses an instrumentation agent to get an accurate estimate of an objects size, plus reflection to + * aggregate the size of object-valued fields. Sharing is not handled in the general case, although + * some special cases are handed, such as the fact that {@link RNull} is a singleton. + * + * In order to satisfy the requirements of the Java instrumentation API, we have to load the agent + * from a jar file. The creation and loading is all orchestrated by this class. + */ +public class AgentObjectSizeFactory extends ObjectSizeFactory { + + private Map<Class<?>, ArrayList<Field>> objectFieldsMap = new HashMap<>(); + private static Map<Class<?>, TypeCustomizer> customizerMap = new HashMap<>(); // system wide + + public AgentObjectSizeFactory() { + if (!ObjSizeAgent.isInitialized()) { + try { + createAgentJar(); + } catch (Exception ex) { + // not available + Utils.rSuicide("failed to load ObjSizeAgent: " + ex.getMessage()); + } + } + } + + /** + * Adds the class file bytes for a given class to a JAR stream. + */ + static void add(JarOutputStream jar, Class<?> c) throws IOException { + String name = c.getName(); + String classAsPath = name.replace('.', '/') + ".class"; + jar.putNextEntry(new JarEntry(classAsPath)); + + InputStream stream = c.getClassLoader().getResourceAsStream(classAsPath); + + int nRead; + byte[] buf = new byte[1024]; + while ((nRead = stream.read(buf, 0, buf.length)) != -1) { + jar.write(buf, 0, nRead); + } + + jar.closeEntry(); + } + + protected void createAgentJar() throws Exception { + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + Attributes mainAttrs = manifest.getMainAttributes(); + mainAttrs.putValue("Agent-Class", ObjSizeAgent.class.getName()); + mainAttrs.putValue("Premain-Class", ObjSizeAgent.class.getName()); + + Path jar = Files.createTempFile("myagent", ".jar"); + try { + JarOutputStream jarStream = new JarOutputStream(new FileOutputStream(jar.toFile()), manifest); + add(jarStream, ObjSizeAgent.class); + jarStream.close(); + + loadAgent(jar); + } finally { + Files.deleteIfExists(jar); + } + } + + public static void loadAgent(Path agent) throws Exception { + String vmName = ManagementFactory.getRuntimeMXBean().getName(); + int p = vmName.indexOf('@'); + String pid = vmName.substring(0, p); + ClassLoader cl = ToolProvider.getSystemToolClassLoader(); + Class<?> c = Class.forName("com.sun.tools.attach.VirtualMachine", true, cl); + Method attach = c.getDeclaredMethod("attach", String.class); + Method loadAgent = c.getDeclaredMethod("loadAgent", String.class, String.class); + Object vm = attach.invoke(null, pid); + loadAgent.invoke(vm, agent.toString(), ""); + } + + @Override + public long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) { + return getObjectSize(obj, obj, ignoreObjectHandler); + } + + private long getObjectSize(Object rootObj, Object obj, IgnoreObjectHandler ignoreObjectHandler) { + try { + long basicSize = ObjSizeAgent.objectSize(obj); + long size = basicSize; + Class<?> klass = obj.getClass(); + if (klass.isArray() && !klass.getComponentType().isPrimitive()) { + for (int i = 0; i < Array.getLength(obj); i++) { + Object elem = Array.get(obj, i); + if (elem == null || isNa(elem)) { + continue; + } else { + size += getObjectSize(rootObj, elem, ignoreObjectHandler); + } + } + } else { + ArrayList<Field> objectFields = objectFieldsMap.get(klass); + if (objectFields == null) { + objectFields = new ArrayList<>(); + findObjectFields(obj.getClass(), objectFields); + objectFieldsMap.put(klass, objectFields); + } + for (Field objectField : objectFields) { + Object fieldObj = objectField.get(obj); + if (fieldObj == null || ignoreObjectHandler.ignore(rootObj, fieldObj)) { + continue; + } else { + TypeCustomizer typeCustomizer = getCustomizer(fieldObj.getClass()); + if (typeCustomizer == null) { + size += getObjectSize(rootObj, fieldObj, ignoreObjectHandler); + } else { + size += typeCustomizer.getObjectSize(fieldObj); + } + } + } + } + return size; + } catch (Throwable t) { + throw RInternalError.shouldNotReachHere(t); + } + + } + + private static boolean isNa(Object elem) { + String typeName = elem.getClass().getSimpleName(); + switch (typeName) { + case "Integer": + return RRuntime.isNA((int) elem); + case "Double": + return RRuntime.isNA((double) elem); + case "String": + return RRuntime.isNA((String) elem); + default: + return false; + } + } + + /** + * Walks the superclass hierarchy of {@code klass} and accumulates all object-valued fields in + * {@code objectFields}. + */ + private static void findObjectFields(Class<?> klass, ArrayList<Field> objectFields) { + if (klass != Object.class) { + findObjectFields(klass.getSuperclass(), objectFields); + Field[] fields = klass.getDeclaredFields(); + for (Field field : fields) { + Class<?> fieldClass = field.getType(); + if (fieldClass.isPrimitive()) { + continue; + } + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers)) { + continue; + } + // check for special case of an completely ignored type + if (getCustomizer(fieldClass) == RObjectSize.IGNORE) { + continue; + } + field.setAccessible(true); + objectFields.add(field); + } + } + } + + private static TypeCustomizer getCustomizer(Class<?> objClass) { + for (Map.Entry<Class<?>, TypeCustomizer> entry : customizerMap.entrySet()) { + if (entry.getKey().isAssignableFrom(objClass)) { + return entry.getValue(); + } + } + return null; + + } + + @Override + public void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer) { + customizerMap.put(klass, typeCustomizer); + } + +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java similarity index 56% rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java index 05b5db6c2ab0659d63d734052b21dd3216ae1553..f6e7cabae776ea3598ae3034f6ee4da2ee799ca1 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java @@ -23,48 +23,56 @@ package com.oracle.truffle.r.runtime.data; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.r.runtime.context.RContext; +import java.util.Deque; +import java.util.concurrent.ConcurrentLinkedDeque; + +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.utilities.CyclicAssumption; import com.oracle.truffle.r.runtime.data.model.RAbstractVector; /** - * Helper for tracing memory related events. All implementors of {@link RAbstractVector} are - * expected to report to {@link MemoryTracer} and others can listen to them through {@link Listener} - * interface. Use method {@link #reportEvents()} to start the tracing. + * Helper for tracing memory copying events, as used by the {@code tracemem} bultin. All + * implementors of {@link RAbstractVector} are expected to report to {@link MemoryCopyTracer} and + * others can listen to them through {@link Listener} interface. Use method + * {@link #setTracingState(boolean)} to enable/disable the tracing. */ -public final class MemoryTracer { - private static Listener listener; - private static final Assumption noMemoryTracingAssumption = Truffle.getRuntime().createAssumption(); +public final class MemoryCopyTracer { + private static Deque<Listener> listeners = new ConcurrentLinkedDeque<>(); + @CompilationFinal private static boolean enabled; + + private static final CyclicAssumption noMemoryCopyTracingAssumption = new CyclicAssumption("data copying"); - private MemoryTracer() { + private MemoryCopyTracer() { // only static methods } /** - * Sets the listener of memory tracing events. For the time being there can only be one - * listener. This can be extended to an array should we need more listeners. + * Adds a listener of memory copying events. */ - public static void setListener(Listener newListener) { - listener = newListener; + public static void addListener(Listener listener) { + listeners.addLast(listener); } /** * After calling this method memory related events will be reported to the listener. This * invalidates global assumption and should be used with caution. */ - public static void reportEvents() { - noMemoryTracingAssumption.invalidate(); + public static void setTracingState(boolean newState) { + if (enabled != newState) { + noMemoryCopyTracingAssumption.invalidate(); + enabled = newState; + } } /** * Reports copy event to the listener. If there are no traced objects, this should turn into - * no-op. TODO might be worth interposing on a change in {@code tracingState} to turn off the - * collection. + * no-op. */ public static void reportCopying(RAbstractVector source, RAbstractVector dest) { - if (!noMemoryTracingAssumption.isValid() && listener != null && RContext.getInstance().stateInstrumentation.getTracingState()) { - listener.reportCopying(source, dest); + if (enabled) { + for (Listener listener : listeners) { + listener.reportCopying(source, dest); + } } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..b6756248470bea75bf369bfd2859bd071c11de48 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java @@ -0,0 +1,52 @@ +/* + * 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.runtime.data; + +import java.lang.instrument.Instrumentation; + +/** + * This is agent class for object sizing. It has to be separate as it is loaded from a jar file. + * This implements the basic call to the JVM for the "struct" part of the object. + * {@link AgentObjectSizeFactory} handles the recursive sizing based on the field/array types. + * + */ +public class ObjSizeAgent { + private static Instrumentation instrumentation; + + public static void premain(@SuppressWarnings("unused") String agentArgs, Instrumentation inst) { + instrumentation = inst; + } + + public static void agentmain(@SuppressWarnings("unused") String agentArgs, Instrumentation inst) { + instrumentation = inst; + } + + public static long objectSize(Object obj) { + return instrumentation.getObjectSize(obj); + } + + static boolean isInitialized() { + return instrumentation != null; + } + +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..18a0cb472db916172c7386ab5e62fb9334a0552f --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java @@ -0,0 +1,53 @@ +/* + * 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.runtime.data; + +import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler; +import com.oracle.truffle.r.runtime.data.RObjectSize.TypeCustomizer; + +public abstract class ObjectSizeFactory { + + static { + final String prop = System.getProperty("fastr.objectsize.factory.class", "com.oracle.truffle.r.runtime.data.AgentObjectSizeFactory"); + try { + theInstance = (ObjectSizeFactory) Class.forName(prop).newInstance(); + } catch (Exception ex) { + // CheckStyle: stop system..print check + System.err.println("Failed to instantiate class: " + prop); + } + } + + private static ObjectSizeFactory theInstance; + + public static ObjectSizeFactory getInstance() { + return theInstance; + } + + /** + * See {@link RObjectSize#getObjectSize}. + */ + public abstract long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler); + + public abstract void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer); + +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..4d7279743c01e4518d20124db73a4676a5c65a0a --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java @@ -0,0 +1,57 @@ +/* + * 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.runtime.data; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import com.oracle.truffle.r.runtime.Utils; +import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler; + +/** + * A debugging tool, logs all calls to {@link #getObjectSize(Object, IgnoreObjectHandler)} to a + * file. + * + */ +public class OutputAgentObjectSizeFactory extends AgentObjectSizeFactory { + + private PrintWriter printWriter; + + public OutputAgentObjectSizeFactory() { + try { + printWriter = new PrintWriter(new FileWriter(Utils.getLogPath("fastr_objectsize.log").toString())); + } catch (IOException ex) { + Utils.rSuicide(ex.getMessage()); + } + + } + + @Override + public long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) { + long size = super.getObjectSize(obj, ignoreObjectHandler); + printWriter.printf("%s: %d\n", obj.getClass().getSimpleName(), (int) size); + printWriter.flush(); + return size; + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java index 1bfe70252de40c3162543da9ef30c416b27154b2..430be1e43025c645c81ffbb1c59aa6f756c8bee6 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java @@ -68,7 +68,7 @@ public final class RComplex extends RScalarVector implements RAbstractComplexVec @Override public RComplexVector materialize() { RComplexVector result = RDataFactory.createComplexVectorFromScalar(this); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } 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 6131402db4e6ecb0b06453b5f497cb289ce5314e..dad4afd45a6e0e52823f4e946f459dd9a5514e1e 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 @@ -23,8 +23,10 @@ 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; @@ -32,7 +34,7 @@ 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.profiles.ConditionProfile; +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; @@ -49,11 +51,6 @@ import com.oracle.truffle.r.runtime.nodes.RNode; public final class RDataFactory { - /** - * Profile for creation tracing; must precede following declarations. - */ - private static final ConditionProfile statsProfile = ConditionProfile.createBinaryProfile(); - public static final boolean INCOMPLETE_VECTOR = false; public static final boolean COMPLETE_VECTOR = true; @@ -498,24 +495,79 @@ public final class RDataFactory { return traceDataCreated(new RExternalPtr(value, tag, RNull.instance)); } - @CompilationFinal private static PerfHandler stats; + /* + * Support for collecting information on allocations in this class. Rprofmem/Rprof register a + * listener when active which, when memory profiling is enabled, is called with the object being + * allocated. Owing to the use of the Assumption, there should be no overhead when disabled. + */ + + private static Deque<Listener> listeners = new ConcurrentLinkedDeque<>(); + @CompilationFinal private static boolean enabled; + private static final CyclicAssumption noAllocationTracingAssumption = new CyclicAssumption("data allocation"); + + public static void setAllocationTracing(boolean newState) { + if (enabled != newState) { + noAllocationTracingAssumption.invalidate(); + enabled = newState; + } + } private static <T> T traceDataCreated(T data) { - if (statsProfile.profile(stats != null)) { - stats.record(data); + if (enabled) { + for (Listener listener : listeners) { + listener.reportAllocation((RTypedValue) data); + } } return data; } + public interface Listener { + /** + * Invoked when an instance of an {@link RTypedValue} is created. Note that the initial + * state of the complex objects, i.e., those with additional {@code Object} subclass fields, + * which may also be {@link RTypedValue} instances is undefined other than by inspection. A + * listener that computes the "size" of an object must take into account that + * {@link RTypedValue} instances passed to a {@code createXXX} method will already have been + * reported, but other data such as {@code int[]} instances for array dimensions will not. + */ + void reportAllocation(RTypedValue data); + } + + /** + * Sets the listener of memory tracing events. For the time being there can only be one + * listener. This can be extended to an array should we need more listeners. + */ + public static void addListener(Listener listener) { + 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 { + 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 - void record(Object data) { + public void reportAllocation(RTypedValue data) { Class<?> klass = data.getClass(); boolean isBounded = data instanceof RAbstractVector; RPerfStats.Histogram hist = histMap.get(klass); @@ -527,17 +579,6 @@ public final class RDataFactory { hist.inc(length); } - @Override - public void initialize(String optionData) { - stats = this; - histMap = new HashMap<>(); - } - - @Override - public String getName() { - return "datafactory"; - } - @Override public void report() { RPerfStats.out().println("Scalar types"); @@ -558,5 +599,6 @@ public final class RDataFactory { } RPerfStats.out().println(); } + } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java index 096356c36aaf5031ea8b85166e820cfbd7d960b5..2caf812292e1c4270b619fe81165d7e1066aa7b2 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java @@ -90,7 +90,7 @@ public final class RDouble extends RScalarVector implements RAbstractDoubleVecto @Override public RDoubleVector materialize() { RDoubleVector result = RDataFactory.createDoubleVectorFromScalar(getValue()); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java index 075d9ee51126ff8bd297ebffb5b11bed48055b13..33ba1ebd83bac82664770babb137ec2908cf5148 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java @@ -88,7 +88,7 @@ public final class RInteger extends RScalarVector implements RAbstractIntVector @Override public RIntVector materialize() { RIntVector result = RDataFactory.createIntVectorFromScalar(value); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java index 05d0e6d1b1d642a39fc13b620d15a3727a3c130d..44e432804a569f815eeb52fe6b9533d3f79c97a4 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java @@ -94,7 +94,7 @@ public final class RLogical extends RScalarVector implements RAbstractLogicalVec @Override public RLogicalVector materialize() { RLogicalVector result = RDataFactory.createLogicalVectorFromScalar(value); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java new file mode 100644 index 0000000000000000000000000000000000000000..14b70ab39912d6a656926780f59ffe37d48e3daa --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java @@ -0,0 +1,133 @@ +/* + * 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.runtime.data; + +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.frame.FrameDescriptor; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.r.runtime.RCaller; +import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; +import com.oracle.truffle.r.runtime.gnur.SEXPTYPE; + +/** + * Support for the sizing of the objects that flow through the interpreter, i.e., mostly + * {@link RTypedValue}, but also including scalar types like {@code String} and dimension data for + * arrays, i.e., {@code int[]}. + * + * The actually implementation is controlled by {@link ObjectSizeFactory} to finesse problems with + * Java VMs that do not support reflection. + * + * Owing to the (implementation) complexity of some of the types, two levels of customization are + * provided: + * <ol> + * <li>A completely custom sizing implementation can be provided for a specific type. This effects + * all sizing computations.</li> + * <li>In any given call to {@link #getObjectSize} an instance of {@IgnoreObjectHandler} can passed. + * This allows some additional dynamic control over certain fields depending of the context of the + * call. For example, when tracking the incremental memory allocation via {@link RDataFactory}, we + * do not want to (double) count fields of type {@link RTypedValue}. However, when computing the + * total size of the object, e.g. for the {@code utils::object.size} builtin, we do want to count + * them.</li> + * </ol> + * + */ +public class RObjectSize { + public static final int INT_SIZE = 32; + public static final int DOUBLE_SIZE = 64; + public static final int BYTE_SIZE = 8; + + public interface TypeCustomizer { + /** + * Allows complete control over sizing of a type registered with + * {@link #registerTypeCustomizer}. + */ + long getObjectSize(Object obj); + } + + public interface IgnoreObjectHandler { + /** + * Controls which fields of an object passed to {@link #getObjectSize} will be ignored. + * {@code rootObject} is the initiating object and {@code obj} is some field of that object + * or one of its components. The return value should be {@code true} if {@code obj} should + * be ignored. + */ + boolean ignore(Object rootObject, Object obj); + } + + /** + * Returns an estimate of the size of the this object, including the size of any object-valued + * fields, recursively. Evidently this is a snapshot and the size can change as, e.g., + * attributes are added/removed. + * + * If called immediately after creation by {@link RDataFactory}, with an + * {@link IgnoreObjectHandler} that ignores objects created separately and, it provides an + * approximation of the incremental memory usage of the system. + * + * @param ignoreObjectHandler An object that is called to decide whether to include the + * contribution a field of this object (and its sub-objects) in the result. Passing + * {@code null} includes everything. N.B. {@code obj} is typed as {@code Object} only + * to allow scalar typed such as {@code String} to be passed. + * + */ + public static long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) { + return (int) ObjectSizeFactory.getInstance().getObjectSize(obj, ignoreObjectHandler); + } + + /** + * Register a {@link TypeCustomizer} for {@code klass} and its subclasses. I.e. and object + * {@code obj} is customized iff {@code klass.isAssignableFrom(obj.getClass())}. + */ + public static void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer) { + ObjectSizeFactory.getInstance().registerTypeCustomizer(klass, typeCustomizer); + } + + /** + * This denotes a special customizer that completely ignores instances of the type and its + * subclasses. It allows a more efficient implementation as the type can be suppressed + * completely from the computation at the time fields of a containing type are analyzed. + */ + public static final TypeCustomizer IGNORE = new TypeCustomizer() { + + @Override + public long getObjectSize(Object obj) { + return 0; + } + + }; + + // TODO construct proper customizers for some of these. + static { + registerTypeCustomizer(Frame.class, IGNORE); + registerTypeCustomizer(FrameDescriptor.class, IGNORE); + registerTypeCustomizer(Node.class, IGNORE); + registerTypeCustomizer(CallTarget.class, IGNORE); + registerTypeCustomizer(RBuiltinDescriptor.class, IGNORE); + registerTypeCustomizer(RPromise.Closure.class, IGNORE); + registerTypeCustomizer(Assumption.class, IGNORE); + registerTypeCustomizer(RCaller.class, IGNORE); + registerTypeCustomizer(SEXPTYPE.class, IGNORE); + } + +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java index d843cacfb937a8518c91151e91e7e33b3c553076..a18f274d43736e0c7e9936de1eaa8d6adf0aa544 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java @@ -65,7 +65,7 @@ public final class RRaw extends RScalarVector implements RAbstractRawVector { @Override public RRawVector materialize() { RRawVector result = RDataFactory.createRawVector(new byte[]{value}); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java index 6e4a7ca5d9590b16c937b8bb62608c6f25ef8cd7..e12aed17f8fbb58a7789401701ae49a288788850 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java @@ -143,28 +143,28 @@ public abstract class RScalarVector extends RScalar implements RAbstractVector { @Override public RVector copyResized(int size, boolean fillNA) { RVector result = materialize().copyResized(size, fillNA); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } @Override public RAbstractVector copyWithNewDimensions(int[] newDimensions) { RAbstractVector result = materialize().copyWithNewDimensions(newDimensions); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } @Override public RVector copyResizedWithDimensions(int[] newDimensions, boolean fillNA) { RVector result = materialize().copyResizedWithDimensions(newDimensions, fillNA); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } @Override public RAbstractVector copyDropAttributes() { RVector result = materialize().copyDropAttributes(); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java index 3dcc48a783d72896755c68f58238721596a0d86c..75a022269b5c8b5057b02859a0d6f750d412e76b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java @@ -81,7 +81,7 @@ public abstract class RSequence implements RAbstractVector { public final RVector createVector() { RVector result = internalCreateVector(); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java index 764f4c4b4ba2c88556989eb83461fdf4b8409bc5..af3281e0c19f749edcdbd5f3ab44ed4562315ce6 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java @@ -74,7 +74,7 @@ public final class RString extends RScalarVector implements RAbstractStringVecto @Override public RStringVector materialize() { RStringVector result = RDataFactory.createStringVector(new String[]{getValue()}, isComplete()); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } 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 159addc3e228b3bda6e2e3bbcc9e9504544c5987..a75b54fdb4bd80a721e5dfb173f611368db7cb62 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 @@ -581,19 +581,19 @@ public abstract class RVector extends RSharingAttributeStorage implements RShare protected final RVector internalCopyAndReport() { RVector result = internalCopy(); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } protected final RVector internalDeepCopyAndReport() { RVector result = internalDeepCopy(); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } protected final RVector internalCopyResizedAndReport(int size, boolean fillNA) { RVector result = internalCopyResized(size, fillNA); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java index 0fe0baccfc90e7e0bb0b1a374322b1d431b37cc2..99383ff1a2bf7e9f9c5afd5ed0158a00ffb02835 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java @@ -22,7 +22,7 @@ */ package com.oracle.truffle.r.runtime.data.closures; -import com.oracle.truffle.r.runtime.data.MemoryTracer; +import com.oracle.truffle.r.runtime.data.MemoryCopyTracer; import com.oracle.truffle.r.runtime.data.RAttributeProfiles; import com.oracle.truffle.r.runtime.data.RAttributes; import com.oracle.truffle.r.runtime.data.RList; @@ -129,14 +129,14 @@ abstract class RToVectorClosure implements RAbstractVector { @Override public final RAbstractVector copy() { RAbstractVector result = vector.copy(); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } @Override public final RVector copyResized(int size, boolean fillNA) { RVector result = vector.copyResized(size, fillNA); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } @@ -152,7 +152,7 @@ abstract class RToVectorClosure implements RAbstractVector { @Override public final RAbstractVector copyDropAttributes() { RAbstractVector result = vector.copyDropAttributes(); - MemoryTracer.reportCopying(this, result); + MemoryCopyTracer.reportCopying(this, result); return result; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java index a2ae57288b474717f66033b2bacd43a882e5e899..a9b3eebf0606c05d5a434500e994a5ab907cc38c 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java @@ -24,11 +24,11 @@ package com.oracle.truffle.r.runtime.data.model; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.r.runtime.RType; -import com.oracle.truffle.r.runtime.data.MemoryTracer; +import com.oracle.truffle.r.runtime.data.MemoryCopyTracer; import com.oracle.truffle.r.runtime.data.RVector; /** - * When implementing, make sure to invoke related {@link MemoryTracer} methods. + * When implementing, make sure to invoke related {@link MemoryCopyTracer} methods. */ public interface RAbstractVector extends RAbstractContainer { 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 09f5a10046d079cd9b7697e1d51e4fb172fb4556..0b601ce31970ad98c9347a687fc8de366dbcef6c 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 @@ -68,6 +68,11 @@ public final class InstrumentationState implements RContext.ContextState { */ private final RprofState rprofState; + /** + * The {@link RprofmemState} state, if any, associated with this {@link RContext}. + */ + private final RprofmemState rprofmemState; + private final TracememContext tracememContext; /** @@ -96,32 +101,72 @@ public final class InstrumentationState implements RContext.ContextState { */ private boolean debugGloballyDisabled; + private abstract static class RprofAdapter { + protected PrintWriter out; + + /** + * Return current output or {@code null} if not profiling. + */ + public PrintWriter out() { + return out; + } + + public void setOut(PrintWriter out) { + this.out = out; + } + } + /** * State used by {@code Rprof}. * */ - public static final class RprofState { - private PrintWriter out; + 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 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 PrintWriter out() { - return out; + public boolean memoryProfiling() { + return memoryQuad != null; + } + + public MemoryQuad memoryQuad() { + return memoryQuad; } public long intervalInMillis() { @@ -138,6 +183,28 @@ public final class InstrumentationState implements RContext.ContextState { } + 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 static class BrowserState { private boolean inBrowser; private String lastEmptyLineCommand = "n"; @@ -162,6 +229,7 @@ public final class InstrumentationState implements RContext.ContextState { private InstrumentationState(Instrumenter instrumenter) { this.instrumenter = instrumenter; this.rprofState = new RprofState(); + this.rprofmemState = new RprofmemState(); this.tracememContext = new TracememContext(); this.browserState = new BrowserState(); } @@ -215,6 +283,10 @@ public final class InstrumentationState implements RContext.ContextState { return rprofState; } + public RprofmemState getRprofmem() { + return rprofmemState; + } + public TracememContext getTracemem() { return tracememContext; }