From fa3f51bc5a0e88e577ca26966d04df85996b6410 Mon Sep 17 00:00:00 2001
From: Zbynek Slajchrt <zbynek.slajchrt@oracle.com>
Date: Thu, 8 Jun 2017 10:58:36 +0200
Subject: [PATCH] Allocations profiler adapted to the Truffle Allocation
 Profiling API

---
 .../oracle/truffle/r/library/utils/Rprof.java |   3 +
 .../r/nodes/builtin/base/BasePackage.java     |  12 +
 .../truffle/r/nodes/builtin/base/IsNA.java    |   2 +-
 .../r/nodes/builtin/fastr/FastRInterop.java   |   2 +-
 .../builtin/fastr/memprof/FastRprofmem.java   |  95 ++++
 .../fastr/memprof/FastRprofmemShow.java       |  85 ++++
 .../fastr/memprof/FastRprofmemSnapshot.java   |  66 +++
 .../fastr/memprof/FastRprofmemSource.java     |  76 ++++
 .../memprof/MemAllocProfilerPrinter.java      | 118 +++++
 .../truffle/r/runtime/context/RContext.java   |  24 ++
 .../truffle/r/runtime/data/RDataFactory.java  |  85 +++-
 .../memprof/MemAllocProfilerInstrument.java   | 128 ++++++
 .../memprof/MemAllocProfilerPaths.java        | 408 ++++++++++++++++++
 .../memprof/MemAllocProfilerStacks.java       | 143 ++++++
 documentation/allocprof.md                    |  83 ++++
 15 files changed, 1322 insertions(+), 8 deletions(-)
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmem.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemShow.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemSnapshot.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemSource.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/MemAllocProfilerPrinter.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerInstrument.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerPaths.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerStacks.java
 create mode 100644 documentation/allocprof.md

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 8ef4e9fb59..f6b5274db4 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
@@ -137,6 +137,9 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFa
     @TruffleBoundary
     public void reportAllocation(RTypedValue data) {
         RprofState profState = RprofState.get();
+        if (profState.memoryQuad == null) {
+            return;
+        }
         long size = RObjectSize.getObjectSize(data, Rprofmem.myIgnoreObjectHandler);
         if (data instanceof RAbstractVector) {
             if (size >= Rprofmem.LARGE_VECTOR) {
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 dd4bcc5fb5..a1be8ea5fe 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
@@ -128,6 +128,14 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastrDqrls;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastrDqrlsNodeGen;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticSpecial;
+import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmem;
+import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmemNodeGen;
+import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmemShow;
+import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmemShowNodeGen;
+import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmemSnapshot;
+import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmemSnapshotNodeGen;
+import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmemSource;
+import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmemSourceNodeGen;
 import com.oracle.truffle.r.nodes.unary.UnaryNotNode;
 import com.oracle.truffle.r.nodes.unary.UnaryNotNodeGen;
 import com.oracle.truffle.r.runtime.RVisibility;
@@ -434,6 +442,10 @@ public class BasePackage extends RBuiltinPackage {
         add(FastRTrace.Untrace.class, FastRTraceFactory.UntraceNodeGen::create);
         add(FastRTree.class, FastRTreeNodeGen::create);
         add(FastRTreeStats.class, FastRTreeStatsNodeGen::create);
+        add(FastRprofmem.class, FastRprofmemNodeGen::create);
+        add(FastRprofmemShow.class, FastRprofmemShowNodeGen::create);
+        add(FastRprofmemSource.class, FastRprofmemSourceNodeGen::create);
+        add(FastRprofmemSnapshot.class, FastRprofmemSnapshotNodeGen::create);
         add(FileFunctions.BaseName.class, FileFunctionsFactory.BaseNameNodeGen::create);
         add(FileFunctions.DirCreate.class, FileFunctionsFactory.DirCreateNodeGen::create);
         add(FileFunctions.DirExists.class, FileFunctionsFactory.DirExistsNodeGen::create);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
index 166728e664..05cbcbc303 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
@@ -207,7 +207,7 @@ public abstract class IsNA extends RBuiltinNode.Arg1 {
     // fallback
     @Fallback
     protected byte isNA(Object value) {
-        warning(RError.Message.IS_NA_TO_NON_VECTOR, ((RTypedValue) value).getRType().getName());
+        warning(RError.Message.IS_NA_TO_NON_VECTOR, value instanceof RTypedValue ? ((RTypedValue) value).getRType().getName() : value);
         return RRuntime.LOGICAL_FALSE;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
index 9cfc3ee40d..dfd0f56b4b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
@@ -204,7 +204,7 @@ public class FastRInterop {
 
         @Specialization(guards = "!isRMissing(value)")
         @TruffleBoundary
-        protected Object exportSymbol(String name, RTypedValue value) {
+        protected Object exportSymbol(String name, TruffleObject value) {
             if (name == null) {
                 throw error(RError.Message.INVALID_ARGUMENT, "name");
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmem.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmem.java
new file mode 100644
index 0000000000..5b5b1c975a
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmem.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016, 2017, 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.memprof;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.eq;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.r.runtime.instrument.memprof.MemAllocProfilerInstrument;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.api.vm.PolyglotRuntime.Instrument;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+@RBuiltin(name = ".fastr.profmem", visibility = OFF, kind = PRIMITIVE, parameterNames = {"on"}, behavior = IO)
+public abstract class FastRprofmem extends RBuiltinNode.Arg1 {
+
+    public static final String STACKS_VIEW = "stacks";
+    public static final String HOTSPOTS_VIEW = "hotspots";
+
+    static {
+        Casts casts = new Casts(FastRprofmem.class);
+        casts.arg("on").asLogicalVector().mustBe(singleElement()).findFirst().map(toBoolean());
+    }
+
+    static void castViewArg(Casts casts) {
+        casts.arg("view").asStringVector().mustBe(singleElement()).findFirst().mustBe(eq("stacks").or(eq("hotspots")));
+    }
+
+    static void castSnapshotArg(Casts casts) {
+        casts.arg("snapshot").mustBe(TruffleObject.class);
+    }
+
+    @Specialization
+    @TruffleBoundary
+    public Object doProfMem(boolean on) {
+        PolyglotEngine vm = RContext.getInstance().getVM();
+        if (vm != null) {
+            Instrument profilerInstr = vm.getRuntime().getInstruments().get(MemAllocProfilerInstrument.ID);
+            if (profilerInstr != null && profilerInstr.isEnabled() != on) {
+                profilerInstr.setEnabled(on);
+            }
+        } else {
+            throw error(RError.Message.GENERIC, "No context VM found");
+        }
+        return RNull.instance;
+    }
+
+    static MemAllocProfilerPrinter getProfilerPrinter() {
+        PolyglotEngine vm = RContext.getInstance().getVM();
+        MemAllocProfilerPrinter profPrinter = null;
+        if (vm != null) {
+            Instrument profilerInstr = vm.getRuntime().getInstruments().get(MemAllocProfilerInstrument.ID);
+            if (profilerInstr != null && profilerInstr.isEnabled()) {
+                profPrinter = profilerInstr.lookup(MemAllocProfilerPrinter.class);
+            }
+        }
+
+        if (profPrinter == null) {
+            profPrinter = new MemAllocProfilerPrinter(System.out);
+        }
+
+        return profPrinter;
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemShow.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemShow.java
new file mode 100644
index 0000000000..b91d1b1de2
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemShow.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016, 2017, 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.memprof;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmem.castSnapshotArg;
+import static com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmem.castViewArg;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.instrument.memprof.MemAllocProfilerPaths;
+
+@RBuiltin(name = ".fastr.profmem.show", visibility = OFF, kind = PRIMITIVE, parameterNames = {"levels", "desc", "id", "printParents", "view", "snapshot"}, behavior = IO)
+public abstract class FastRprofmemShow extends RBuiltinNode.Arg6 {
+
+    static {
+        Casts casts = new Casts(FastRprofmemShow.class);
+        casts.arg("levels").asIntegerVector().mustBe(singleElement()).findFirst().replaceNA(Integer.MAX_VALUE);
+        casts.arg("desc").asLogicalVector().mustBe(singleElement()).findFirst().map(toBoolean());
+        casts.arg("id").returnIf(nullValue()).asIntegerVector().mustBe(singleElement()).findFirst().replaceNA(Integer.MAX_VALUE);
+        casts.arg("printParents").asLogicalVector().mustBe(singleElement()).findFirst().map(toBoolean());
+        castViewArg(casts);
+        castSnapshotArg(casts);
+    }
+
+    @Override
+    public Object[] getDefaultParameterValues() {
+        return new Object[]{RRuntime.INT_NA, RRuntime.LOGICAL_TRUE, RNull.instance, RRuntime.LOGICAL_FALSE, FastRprofmem.STACKS_VIEW, RNull.instance};
+    }
+
+    @Specialization
+    public Object doProfMem(int levels, boolean desc, @SuppressWarnings("unused") RNull n, boolean printParents, String view, TruffleObject snapshot) {
+        return show(levels, desc, null, printParents, view, snapshot);
+    }
+
+    @Specialization
+    public Object doProfMem(int levels, boolean desc, int entryId, boolean printParents, String view, TruffleObject snapshot) {
+        return show(levels, desc, entryId, printParents, view, snapshot);
+    }
+
+    @TruffleBoundary
+    private static Object show(int levels, boolean desc, Integer entryId, boolean printParents, String view, TruffleObject snapshotTO) {
+        MemAllocProfilerPaths snapshot = MemAllocProfilerPaths.fromTruffleObject(snapshotTO);
+        return show(levels, desc, entryId, printParents, view, snapshot);
+    }
+
+    private static Object show(int levels, boolean desc, Integer entryId, boolean printParents, String view, MemAllocProfilerPaths snapshot) {
+        MemAllocProfilerPaths usedSnapshot = snapshot;
+        if (FastRprofmem.HOTSPOTS_VIEW.equals(view)) {
+            usedSnapshot = usedSnapshot.toHotSpots();
+        }
+        FastRprofmem.getProfilerPrinter().show(usedSnapshot, entryId, levels, desc, printParents);
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemSnapshot.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemSnapshot.java
new file mode 100644
index 0000000000..9c04dc658b
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemSnapshot.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016, 2017, 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.memprof;
+
+import static com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmem.castViewArg;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.instrument.memprof.MemAllocProfilerPaths;
+import com.oracle.truffle.r.runtime.instrument.memprof.MemAllocProfilerStacks;
+
+@RBuiltin(name = ".fastr.profmem.snapshot", visibility = OFF, kind = PRIMITIVE, parameterNames = {"name", "view"}, behavior = IO)
+public abstract class FastRprofmemSnapshot extends RBuiltinNode.Arg2 {
+
+    static {
+        Casts casts = new Casts(FastRprofmemSnapshot.class);
+        casts.arg("name").asStringVector().mustBe(singleElement()).findFirst();
+        castViewArg(casts);
+    }
+
+    @Override
+    public Object[] getDefaultParameterValues() {
+        return new Object[]{RNull.instance, FastRprofmem.STACKS_VIEW};
+    }
+
+    @Specialization
+    @TruffleBoundary
+    public TruffleObject makeSnapshot(String name, String view) {
+        MemAllocProfilerPaths snapshot = MemAllocProfilerStacks.getInstance().getStackPaths().getOrMakeSnapshot(name);
+
+        if (FastRprofmem.HOTSPOTS_VIEW.equals(view)) {
+            snapshot = snapshot.toHotSpots();
+        }
+
+        return snapshot.toTruffleObject();
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemSource.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemSource.java
new file mode 100644
index 0000000000..5339bbaac4
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/FastRprofmemSource.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016, 2017, 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.memprof;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmem.castSnapshotArg;
+import static com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmem.castViewArg;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.instrument.memprof.MemAllocProfilerPaths;
+
+@RBuiltin(name = ".fastr.profmem.source", visibility = OFF, kind = PRIMITIVE, parameterNames = {"id", "view", "snapshot"}, behavior = IO)
+public abstract class FastRprofmemSource extends RBuiltinNode.Arg3 {
+
+    static {
+        Casts casts = new Casts(FastRprofmemSource.class);
+        casts.arg("id").asIntegerVector().mustBe(singleElement()).findFirst().replaceNA(Integer.MAX_VALUE);
+        castViewArg(casts);
+        castSnapshotArg(casts);
+    }
+
+    @Override
+    public Object[] getDefaultParameterValues() {
+        return new Object[]{RRuntime.INT_NA, FastRprofmem.STACKS_VIEW, RNull.instance};
+    }
+
+    @Specialization
+    @TruffleBoundary
+    public Object showSource(int entryId, String view, TruffleObject snapshotTO) {
+        MemAllocProfilerPaths paths = MemAllocProfilerPaths.fromTruffleObject(snapshotTO);
+        return showSource(entryId, view, paths);
+
+    }
+
+    private static Object showSource(int entryId, String view, MemAllocProfilerPaths snap) {
+        MemAllocProfilerPaths snapshot = snap;
+        if (FastRprofmem.HOTSPOTS_VIEW.equals(view)) {
+            snapshot = snapshot.toHotSpots();
+        }
+
+        FastRprofmem.getProfilerPrinter().source(snapshot, entryId);
+
+        return RNull.instance;
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/MemAllocProfilerPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/MemAllocProfilerPrinter.java
new file mode 100644
index 0000000000..c3cda91fda
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/memprof/MemAllocProfilerPrinter.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2017, 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.memprof;
+
+import java.io.PrintStream;
+import java.util.Iterator;
+
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.instrument.memprof.MemAllocProfilerPaths;
+import com.oracle.truffle.r.runtime.instrument.memprof.MemAllocProfilerStacks;
+
+/**
+ * A utility class that can be used for developing simple command line profiling tools. It works on
+ * top of the {@link MemAllocProfilerStacks memory allocations model}.
+ *
+ * <pre>
+ * MemAllocProfilerPrinter memAllocPrinter = new MemAllocProfilerPrinter(System.out);
+ * // Print the whole hierarchy of allocations
+ * memAllocPrinter.show(null, entryId.MAX_VALUE, true, true);
+ * // Print the source section associated with the allocation entry 103
+ * memAllocPrinter.source(103);
+ * </pre>
+ */
+public final class MemAllocProfilerPrinter {
+
+    private final PrintStream out;
+
+    /**
+     * @param out the output stream used to print the output
+     */
+    public MemAllocProfilerPrinter(PrintStream out) {
+        this.out = out;
+    }
+
+    /**
+     * Show the allocations hierarchy.
+     *
+     * @param paths the allocation paths
+     * @param entryId the id of the top allocation entry. If <code>null</code> the absolute root
+     *            entry is used.
+     * @param levels the maximum number of levels of the hierarchy to print
+     * @param desc if true allocation entries will be sorted in the descending order below their
+     *            parent entries
+     * @param printParents instructs to print the parent entries of the top entry. It makes sense
+     *            only if a sub-hierarchy is printed.
+     */
+    public void show(MemAllocProfilerPaths paths, Integer entryId, int levels, boolean desc, boolean printParents) {
+        MemAllocProfilerPaths.Entry rootEntry = null;
+        if (entryId != null) {
+            rootEntry = paths.getEntry(entryId);
+        }
+
+        paths.traverse(rootEntry, stack -> {
+            if (stack.peek().getStats().getCount() == 0 && stack.size() != levels) {
+                return;
+            }
+
+            StringBuilder sb = new StringBuilder();
+            Iterator<MemAllocProfilerPaths.Entry> iter = stack.descendingIterator();
+            while (iter.hasNext()) {
+                MemAllocProfilerPaths.Entry entry = iter.next();
+                if (entry.getId() == 0) {
+                    continue;
+                }
+                sb.append('/').append(entry.getName()).append("[id=").append(entry.getId()).append(']');
+            }
+            MemAllocProfilerPaths.Entry entry = stack.peek();
+            sb.append(" { size: ").append(entry.getAllocatedAggr()).append(", count: ").append(entry.getCountAggr()).append(" }");
+            out.println(sb.toString());
+        }, new MemAllocProfilerStacks.AlocatedAggrComparator(desc), levels, printParents);
+    }
+
+    /**
+     * Print the source section associated with the given allocation entry id.
+     *
+     * @param entryId the entry id
+     */
+    public void source(MemAllocProfilerPaths paths, int entryId) {
+        MemAllocProfilerPaths.Entry entry = paths.getEntry(entryId);
+        if (entry == null) {
+            out.format("No entry found for id %s\n", entryId);
+        } else {
+            SourceSection sel = entry.getSourceSection();
+            StringBuilder sb = new StringBuilder();
+            sb.append("{ size: ").append(entry.getAllocatedAggr()).append(", localSize: ").append(entry.getStats().getAllocated()).append(", count: ").append(entry.getCountAggr()).append(
+                            ", localCount: ").append(entry.getStats().getCount()).append(" }");
+            out.println(sb);
+
+            if (sel == null) {
+                out.println("No source available");
+            } else {
+                out.format("<<< %s at %s:%s\n", entry.getName(), sel.getStartLine(), sel.getStartColumn());
+                out.println(sel.getCode());
+                out.format(">>> %s at %s:%s\n", entry.getName(), sel.getEndLine(), sel.getEndColumn());
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
index 7a74c11d3d..b8f37f4b22 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
@@ -22,6 +22,8 @@
  */
 package com.oracle.truffle.r.runtime.context;
 
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 import java.io.Closeable;
 import java.lang.ref.WeakReference;
 import java.nio.file.Path;
@@ -36,6 +38,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.TruffleLanguage.Env;
+import com.oracle.truffle.api.instrumentation.AllocationReporter;
 import com.oracle.truffle.api.instrumentation.Instrumenter;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
@@ -62,6 +65,7 @@ import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
@@ -69,6 +73,7 @@ import com.oracle.truffle.r.runtime.instrument.InstrumentationState;
 import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 import com.oracle.truffle.r.runtime.rng.RRNG;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
 
 /**
  * Encapsulates the runtime state ("context") of an R session. All access to that state from the
@@ -327,6 +332,8 @@ public final class RContext implements RTruffleObject {
     public final WeakHashMap<Source, REnvironment> sourceRefEnvironments = new WeakHashMap<>();
     public final WeakHashMap<Path, REnvironment> srcfileEnvironments = new WeakHashMap<>();
 
+    private final AllocationReporter allocationReporter;
+
     private ContextState[] contextStates() {
         return new ContextState[]{stateREnvVars, stateRProfile, stateTempPath, stateROptions, stateREnvironment, stateRErrorHandling, stateRConnection, stateStdConnections, stateRNG, stateRFFI,
                         stateRSerialize,
@@ -381,6 +388,9 @@ public final class RContext implements RTruffleObject {
         this.stateDLL = DLL.ContextStateImpl.newContextState();
         this.engine = RContext.getRRuntimeASTAccess().createEngine(this);
         state.add(State.CONSTRUCTED);
+
+        this.allocationReporter = env.lookup(AllocationReporter.class);
+        this.allocationReporter.addPropertyChangeListener(ALLOCATION_ACTIVATION_LISTENER);
     }
 
     /**
@@ -522,6 +532,8 @@ public final class RContext implements RTruffleObject {
                 threadLocalContext.set(info.getParent());
             }
             state = EnumSet.of(State.DESTROYED);
+
+            this.allocationReporter.removePropertyChangeListener(ALLOCATION_ACTIVATION_LISTENER);
         }
     }
 
@@ -785,4 +797,16 @@ public final class RContext implements RTruffleObject {
     public static Class<? extends TruffleRLanguage> getTruffleRLanguage() {
         return getRRuntimeASTAccess().getTruffleRLanguage();
     }
+
+    public AllocationReporter getAllocationReporter() {
+        return this.allocationReporter;
+    }
+
+    private static final PropertyChangeListener ALLOCATION_ACTIVATION_LISTENER = new PropertyChangeListener() {
+        @Override
+        public void propertyChange(PropertyChangeEvent event) {
+            RDataFactory.setTracingState(event.getNewValue() == Boolean.TRUE);
+        }
+    };
+
 }
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 2192e8f059..c3c0ff363a 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
@@ -33,6 +33,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.instrumentation.AllocationReporter;
 import com.oracle.truffle.api.utilities.CyclicAssumption;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -40,9 +41,11 @@ 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.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.context.RContext;
 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.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
@@ -157,6 +160,7 @@ public final class RDataFactory {
     }
 
     public static RComplexVector createComplexVector(double[] data, boolean complete, int[] dims, RStringVector names) {
+
         return traceDataCreated(new RComplexVector(data, complete, dims, names));
     }
 
@@ -219,6 +223,7 @@ public final class RDataFactory {
     }
 
     public static RLogicalVector createLogicalVector(byte[] data, boolean complete, int[] dims, RStringVector names) {
+
         return traceDataCreated(new RLogicalVector(data, complete, dims, names));
     }
 
@@ -228,25 +233,30 @@ public final class RDataFactory {
 
     public static RIntSequence createAscendingRange(int start, int end) {
         assert start <= end;
+
         return traceDataCreated(new RIntSequence(start, 1, end - start + 1));
     }
 
     public static RIntSequence createDescendingRange(int start, int end) {
         assert start > end;
+
         return traceDataCreated(new RIntSequence(start, -1, start - end + 1));
     }
 
     public static RIntSequence createIntSequence(int start, int stride, int length) {
+
         return traceDataCreated(new RIntSequence(start, stride, length));
     }
 
     public static RDoubleSequence createAscendingRange(double start, double end) {
         assert start <= end;
+
         return traceDataCreated(new RDoubleSequence(start, 1, (int) ((end - start) + 1)));
     }
 
     public static RDoubleSequence createDescendingRange(double start, double end) {
         assert start > end;
+
         return traceDataCreated(new RDoubleSequence(start, -1, (int) ((start - end) + 1)));
     }
 
@@ -388,6 +398,7 @@ public final class RDataFactory {
     }
 
     public static RList createList(Object[] data, int[] newDimensions, RStringVector names) {
+
         return traceDataCreated(new RList(data, newDimensions, names));
     }
 
@@ -462,11 +473,19 @@ public final class RDataFactory {
     }
 
     public static Object createLangPairList(int size) {
-        return size == 0 ? RNull.instance : traceDataCreated(RPairList.create(size, SEXPTYPE.LANGSXP));
+        if (size == 0) {
+            return RNull.instance;
+        } else {
+            return traceDataCreated(RPairList.create(size, SEXPTYPE.LANGSXP));
+        }
     }
 
     public static Object createPairList(int size) {
-        return size == 0 ? RNull.instance : traceDataCreated(RPairList.create(size));
+        if (size == 0) {
+            return RNull.instance;
+        } else {
+            return traceDataCreated(RPairList.create(size));
+        }
     }
 
     public static RPairList createPairList() {
@@ -474,6 +493,7 @@ public final class RDataFactory {
     }
 
     public static RPairList createPairList(Object car) {
+
         return traceDataCreated(new RPairList(car, RNull.instance, RNull.instance, null));
     }
 
@@ -546,25 +566,54 @@ public final class RDataFactory {
      */
 
     private static Deque<Listener> listeners = new ConcurrentLinkedDeque<>();
-    @CompilationFinal private static boolean enabled;
+
+    @CompilationFinal private static byte enabled = RRuntime.LOGICAL_NA;
+
     private static final CyclicAssumption noAllocationTracingAssumption = new CyclicAssumption("data allocation");
 
     public static void setTracingState(boolean newState) {
-        if (enabled != newState) {
+        byte newStateLogical = RRuntime.asLogical(newState);
+        if (enabled != newStateLogical) {
             noAllocationTracingAssumption.invalidate();
-            enabled = newState;
+            enabled = newStateLogical;
         }
     }
 
     private static <T> T traceDataCreated(T data) {
-        if (enabled) {
+        if (RRuntime.isNA(enabled)) {
+            RContext ctx = RContext.getThreadLocalInstance();
+            if (ctx != null) {
+                setTracingState(ctx.getAllocationReporter().isActive());
+            }
+        }
+
+        if (enabled == RRuntime.LOGICAL_TRUE) {
+
+            if (noAllocationTracingAssumption.getAssumption().isValid()) {
+                reportAllocation(data);
+            }
+
             for (Listener listener : listeners) {
                 listener.reportAllocation((RTypedValue) data);
             }
         }
+
         return data;
     }
 
+    @TruffleBoundary
+    private static void reportAllocation(Object data) {
+        RContext ctx = RContext.getThreadLocalInstance();
+        assert ctx != null;
+        AllocationReporter allocationReporter = ctx.getAllocationReporter();
+
+        allocationReporter.onEnter(null, 0, AllocationReporter.SIZE_UNKNOWN);
+
+        long size = data instanceof RTypedValue ? getSize((RTypedValue) data) : AllocationReporter.SIZE_UNKNOWN;
+        allocationReporter.onReturnValue(data, 0, size);
+
+    }
+
     public interface Listener {
         /**
          * Invoked when an instance of an {@link RTypedValue} is created. Note that the initial
@@ -590,4 +639,28 @@ public final class RDataFactory {
         Arrays.fill(data, RNull.instance);
         return data;
     }
+
+    private static long getSize(RTypedValue data) {
+        long multiplier = 8;
+        switch (data.getRType()) {
+            case Complex:
+                multiplier = 16;
+                break;
+            case Integer:
+                multiplier = 4;
+                break;
+            case Logical:
+            case Raw:
+                multiplier = 1;
+                break;
+        }
+        if (data instanceof RSequence) {
+            return 32 + 2 * multiplier;
+        } else if (data instanceof RAbstractVector) {
+            return 32 + ((RAbstractVector) data).getLength() * multiplier;
+        } else {
+            // take a default value for non-vector objects
+            return 64;
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerInstrument.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerInstrument.java
new file mode 100644
index 0000000000..1176384364
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerInstrument.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2017, 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.instrument.memprof;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrumentation.AllocationEvent;
+import com.oracle.truffle.api.instrumentation.AllocationEventFilter;
+import com.oracle.truffle.api.instrumentation.AllocationListener;
+import com.oracle.truffle.api.instrumentation.EventBinding;
+import com.oracle.truffle.api.instrumentation.EventContext;
+import com.oracle.truffle.api.instrumentation.ExecutionEventNode;
+import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
+import com.oracle.truffle.api.instrumentation.Instrumenter;
+import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
+import com.oracle.truffle.api.instrumentation.StandardTags;
+import com.oracle.truffle.api.instrumentation.TruffleInstrument;
+import com.oracle.truffle.api.instrumentation.TruffleInstrument.Registration;
+import com.oracle.truffle.api.source.SourceSection;
+
+@Registration(name = "MemAllocProfiler", id = MemAllocProfilerInstrument.ID)
+public class MemAllocProfilerInstrument extends TruffleInstrument {
+
+    public static final String ID = "mem-alloc-profiler";
+
+    private EventBinding<MemAllocEventFactory> allocationEventBinding;
+
+    @Override
+    protected void onCreate(TruffleInstrument.Env env) {
+        env.registerService(this);
+
+        Instrumenter instrumenter = env.getInstrumenter();
+        MemAllocEventFactory eventFactory = new MemAllocEventFactory(env);
+        SourceSectionFilter.Builder builder = SourceSectionFilter.newBuilder();
+        SourceSectionFilter filter = builder.tagIs(StandardTags.StatementTag.class).build();
+        instrumenter.attachFactory(filter, eventFactory);
+        allocationEventBinding = instrumenter.attachAllocationListener(AllocationEventFilter.newBuilder().build(), eventFactory);
+
+        env.registerService(eventFactory.memAllocStacks);
+    }
+
+    @Override
+    protected void onDispose(Env env) {
+        allocationEventBinding.dispose();
+    }
+
+    public static class MemAllocEventFactory implements ExecutionEventNodeFactory, AllocationListener {
+
+        protected final Env env;
+        protected final MemAllocProfilerStacks memAllocStacks = MemAllocProfilerStacks.getInstance();
+
+        protected MemAllocEventFactory(final Env env) {
+            this.env = env;
+        }
+
+        @Override
+        @TruffleBoundary
+        public void onEnter(AllocationEvent event) {
+        }
+
+        @Override
+        @TruffleBoundary
+        public void onReturnValue(AllocationEvent event) {
+            memAllocStacks.reportAllocation(event.getNewSize() - event.getOldSize());
+        }
+
+        public MemAllocProfilerStacks getStacks() {
+            return memAllocStacks;
+        }
+
+        public void dispose() {
+            memAllocStacks.clear();
+        }
+
+        @Override
+        public ExecutionEventNode create(final EventContext ec) {
+            return new ExecutionEventNode() {
+
+                @Override
+                protected void onEnter(VirtualFrame frame) {
+                    pushEntry();
+                }
+
+                @Override
+                public void onReturnValue(VirtualFrame vFrame, Object result) {
+                    popEntry();
+                }
+
+                @Override
+                protected void onReturnExceptional(VirtualFrame frame, Throwable exception) {
+                    popEntry();
+                }
+
+                @TruffleBoundary
+                private void pushEntry() {
+                    SourceSection src = ec.getInstrumentedSourceSection();
+                    memAllocStacks.push(ec.getInstrumentedNode().getRootNode().getName(), src);
+                }
+
+                @TruffleBoundary
+                private void popEntry() {
+                    memAllocStacks.pop();
+                }
+
+            };
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerPaths.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerPaths.java
new file mode 100644
index 0000000000..75ca4b5c90
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerPaths.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2017, 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.instrument.memprof;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+
+public final class MemAllocProfilerPaths {
+    private final AtomicLong version = new AtomicLong();
+    private final AtomicInteger idGen = new AtomicInteger();
+    private final Map<Integer, Entry> entryMap = new ConcurrentHashMap<>();
+    private volatile Entry root = new Entry(this, null, "", null);
+
+    private MemAllocProfilerPaths hotSpotsView;
+    private long hotSpotsViewVersion;
+
+    /**
+     * Clear the model.
+     */
+    public synchronized void clear() {
+        idGen.set(0);
+        entryMap.clear();
+        root = new Entry(this, null, "", null);
+    }
+
+    /**
+     *
+     * @param id the id of the allocation entry
+     * @return the allocation entry
+     */
+    public Entry getEntry(int id) {
+        return entryMap.get(id);
+    }
+
+    /**
+     * @return the root entry.
+     */
+    public Entry getRootEntry() {
+        return root;
+    }
+
+    public TruffleObject toTruffleObject() {
+        return JavaInterop.asTruffleObject(this);
+    }
+
+    public static MemAllocProfilerPaths fromTruffleObject(TruffleObject to) {
+        return JavaInterop.asJavaObject(MemAllocProfilerPaths.class, to);
+    }
+
+    /**
+     * Store the snapshot of the current paths hierarchy.
+     *
+     * @param name the snapshot name
+     * @return the snapshot
+     */
+    public MemAllocProfilerPaths getOrMakeSnapshot(String name) {
+        return clonePaths();
+    }
+
+    public synchronized MemAllocProfilerPaths toHotSpots() {
+        long curVer = version.get();
+        if (curVer == hotSpotsViewVersion && hotSpotsView != null) {
+            return hotSpotsView;
+        }
+        hotSpotsViewVersion = curVer;
+        hotSpotsView = invert().groupBySrcSection();
+        return hotSpotsView;
+    }
+
+    @SuppressWarnings("unused")
+    synchronized MemAllocProfilerPaths clonePaths() {
+        MemAllocProfilerPaths clonedPaths = new MemAllocProfilerPaths();
+        new Entry(clonedPaths, null, root);
+        return clonedPaths;
+    }
+
+    MemAllocProfilerPaths invert() {
+        List<Entry> inverted = new ArrayList<>();
+        MemAllocProfilerPaths targetPaths = new MemAllocProfilerPaths();
+        for (Entry entry : entryMap.values()) {
+            inverted.add(entry.invert(targetPaths));
+        }
+        return targetPaths;
+    }
+
+    MemAllocProfilerPaths groupBySrcSection() {
+        MemAllocProfilerPaths targetPaths = new MemAllocProfilerPaths();
+
+        List<Entry> tops = new ArrayList<>();
+        for (Entry synthSubRoot : root.children.values()) {
+            tops.addAll(synthSubRoot.children.values());
+        }
+
+        groupBySrcSection(targetPaths, targetPaths.root, tops);
+        return targetPaths;
+    }
+
+    private void groupBySrcSection(MemAllocProfilerPaths targetPaths, Entry newParent, Collection<Entry> nonGroupedEntries) {
+        Map<SourceSection, List<Entry>> entriesBySection = new HashMap<>();
+
+        for (Entry nonGroupedEntry : nonGroupedEntries) {
+            List<Entry> group = entriesBySection.get(nonGroupedEntry.sourceSection);
+            if (group == null) {
+                group = new ArrayList<>();
+                entriesBySection.put(nonGroupedEntry.sourceSection, group);
+            }
+            group.add(nonGroupedEntry);
+        }
+
+        for (Map.Entry<SourceSection, List<Entry>> mapEntry : entriesBySection.entrySet()) {
+            List<Entry> nonGroupedChildren = new ArrayList<>();
+            Entry reducedEntry = null;
+            for (Entry entryForSameSect : mapEntry.getValue()) {
+                if (reducedEntry == null) {
+                    reducedEntry = new Entry(targetPaths, newParent, entryForSameSect.name, entryForSameSect.sourceSection);
+                }
+                reducedEntry.stats.add(entryForSameSect.stats);
+                nonGroupedChildren.addAll(entryForSameSect.children.values());
+            }
+            assert reducedEntry != null;
+            groupBySrcSection(targetPaths, reducedEntry, nonGroupedChildren);
+        }
+    }
+
+    /**
+     * Traverse the allocations hierarchy. This method is usually used by profiling tools for
+     * displaying the allocations.
+     *
+     * @param startEntry the start entry from which the traversal begins. It can be null to denote
+     *            the absolute root.
+     * @param consumer the consumer receiving the current stack (i.e. the path of entries to the
+     *            root entry)
+     * @param childrenEntriesComparator the comparator used to sort children
+     * @param levels the maximum number of hierarchy levels to traverse
+     * @param prependParentEntries include the parent entries of the root entry
+     */
+    public void traverse(Entry startEntry, Consumer<Deque<Entry>> consumer, Comparator<Entry> childrenEntriesComparator, int levels, boolean prependParentEntries) {
+        Entry se = startEntry;
+        if (se == null) {
+            se = root;
+        }
+        traverseEntry(se, consumer, childrenEntriesComparator, levels, prependParentEntries);
+    }
+
+    private void traverseEntry(Entry rootEntry, Consumer<Deque<Entry>> consumer, Comparator<Entry> childrenEntriesComparator, int levels, boolean prependParentEntries) {
+        ArrayDeque<Entry> stack = new ArrayDeque<>();
+        if (prependParentEntries) {
+            Entry e = rootEntry;
+            while (e != null) {
+                stack.addLast(e);
+                e = e.parent;
+            }
+        } else {
+            stack.push(rootEntry);
+        }
+        traverseStack(stack, consumer, childrenEntriesComparator, levels);
+    }
+
+    private void traverseStack(Deque<Entry> stack, Consumer<Deque<Entry>> consumer, Comparator<Entry> childrenEntriesComparator, int levels) {
+        if (levels <= 0) {
+            return;
+        }
+
+        consumer.accept(stack);
+        Collection<Entry> children = stack.peek().children.values();
+        Collection<Entry> sortedChildren;
+
+        if (childrenEntriesComparator != null) {
+            sortedChildren = new TreeSet<>(childrenEntriesComparator);
+            sortedChildren.addAll(children);
+        } else {
+            sortedChildren = children;
+        }
+
+        for (Entry childEntry : sortedChildren) {
+            stack.push(childEntry);
+            traverseStack(stack, consumer, childrenEntriesComparator, levels - 1);
+            stack.pop();
+        }
+    }
+
+    public static final class Entry {
+        // private static final SourceSection UNAVAILABLE_SECTION =
+        // Source.newBuilder("").name("unavailable").mimeType("").build().createUnavailableSection();
+
+        final MemAllocProfilerPaths paths;
+        final int id;
+        final String name;
+        final SourceSection sourceSection;
+        final Entry parent;
+        final Map<SourceSection, Entry> children = new ConcurrentHashMap<>();
+        final Stats stats = new Stats() {
+
+            @Override
+            public void set(long allocated, long count) {
+                synchronized (paths) {
+                    super.set(allocated, count);
+                    paths.version.incrementAndGet();
+                }
+            }
+
+        };
+
+        Entry(MemAllocProfilerPaths paths, Entry parent, String name, SourceSection sourceSection) {
+            this.paths = paths;
+            synchronized (paths) {
+                this.id = paths.idGen.getAndIncrement();
+                this.parent = parent;
+                this.name = name;
+                this.sourceSection = sourceSection == null ? Source.newBuilder("").name(name).mimeType("").build().createUnavailableSection() : sourceSection;
+                this.paths.entryMap.put(id, this);
+                if (parent != null) {
+                    parent.children.put(this.sourceSection, this);
+                }
+            }
+        }
+
+        @SuppressWarnings("unused")
+        Entry(MemAllocProfilerPaths paths, Entry parent, Entry original) {
+            this.paths = paths;
+            this.parent = parent;
+            assert parent == null || parent.paths == paths;
+            this.id = original.id;
+            this.name = original.name;
+            this.sourceSection = original.sourceSection;
+            this.paths.entryMap.put(id, this);
+            this.stats.add(original.stats);
+
+            if (parent != null) {
+                parent.children.put(sourceSection, this);
+            } else {
+                paths.root = this;
+            }
+
+            for (Entry origChild : original.children.values()) {
+                new Entry(paths, this, origChild);
+            }
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public SourceSection getSourceSection() {
+            return sourceSection;
+        }
+
+        public Entry getParent() {
+            return parent;
+        }
+
+        public Map<SourceSection, Entry> getChildren() {
+            return children;
+        }
+
+        public synchronized Entry[] getChildrenAsArray() {
+            return children.values().toArray(new Entry[children.size()]);
+        }
+
+        public Stats getStats() {
+            return stats;
+        }
+
+        public long getAllocatedAggr() {
+            long a = stats.allocated;
+            for (Entry childEntry : children.values()) {
+                a += childEntry.getAllocatedAggr();
+            }
+            return a;
+        }
+
+        public long getCountAggr() {
+            long a = stats.count;
+            for (Entry childEntry : children.values()) {
+                a += childEntry.getCountAggr();
+            }
+            return a;
+        }
+
+        public long getAllocated() {
+            return stats.allocated;
+        }
+
+        public long getCount() {
+            return stats.count;
+        }
+
+        Entry invert(MemAllocProfilerPaths targetPaths) {
+            return invert(targetPaths, new Entry(targetPaths, targetPaths.root, "", null), stats);
+        }
+
+        private Entry invert(MemAllocProfilerPaths targetPaths, Entry newParent, Stats rootStat) {
+            if (this == paths.root) {
+                return targetPaths.root;
+            }
+            Entry ie = new Entry(targetPaths, newParent, name, sourceSection);
+            if (parent.parent != null) {
+                Entry invertedParent = parent.invert(targetPaths, ie, rootStat);
+                ie.children.put(parent.sourceSection, invertedParent);
+            } else {
+                // Move the stats from the inverted node to its lowest child (which corresponds to
+                // the root in the original hierarchy). This way the stats of a parent entry in the
+                // hot-spot paths will be the aggregation of the children stats, i.e. the same fact
+                // as in the stacks paths.
+                ie.stats.add(rootStat);
+            }
+            return ie;
+        }
+
+        Entry merge(MemAllocProfilerPaths targetPaths, Entry other) {
+            Entry merged = new Entry(targetPaths, this.parent, this.name, this.sourceSection);
+            merged.children.putAll(this.children);
+            merged.children.putAll(other.children);
+            merged.stats.add(this.stats);
+            merged.stats.add(other.stats);
+            return merged;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("{name: %s, alloc: %s, count: %s}", name, stats.allocated, stats.count);
+        }
+    }
+
+    /**
+     * The summary allocation statistic.
+     */
+    public static class Stats {
+        private volatile long allocated;
+        private volatile long count;
+
+        /**
+         * @return the allocated memory in bytes
+         */
+        public final long getAllocated() {
+            return this.allocated;
+        }
+
+        /**
+         * @return the number of allocations
+         */
+        public final long getCount() {
+            return count;
+        }
+
+        /**
+         * Set the allocated memory in bytes and allocations count.
+         */
+        public void set(long allocated, long count) {
+            this.allocated = allocated;
+            this.count = count;
+        }
+
+        final void clear() {
+            set(0, 0);
+        }
+
+        final void add(long alloc, long cnt) {
+            set(this.allocated + alloc, this.count + cnt);
+        }
+
+        final void add(Stats other) {
+            add(other.allocated, other.count);
+        }
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerStacks.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerStacks.java
new file mode 100644
index 0000000000..2a4ee643bf
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/memprof/MemAllocProfilerStacks.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017, 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.instrument.memprof;
+
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+import com.oracle.truffle.r.runtime.instrument.memprof.MemAllocProfilerPaths.Entry;
+import com.oracle.truffle.r.runtime.instrument.memprof.MemAllocProfilerPaths.Stats;
+import com.oracle.truffle.api.source.SourceSection;
+
+/**
+ * This class represents the hierarchical model of memory allocations. It is designed as a
+ * singleton, since it is assumed that there is at most one profiling session at any moment.
+ */
+public final class MemAllocProfilerStacks {
+
+    private static final MemAllocProfilerStacks instance = new MemAllocProfilerStacks();
+
+    final MemAllocProfilerPaths stackPaths = new MemAllocProfilerPaths();
+    final ConcurrentHashMap<Thread, Deque<Entry>> stacks = new ConcurrentHashMap<>();
+    final MemAllocProfilerPaths.Stats globalStats = new MemAllocProfilerPaths.Stats();
+
+    private MemAllocProfilerStacks() {
+
+    }
+
+    /**
+     * @return the single instance of the model
+     */
+    public static MemAllocProfilerStacks getInstance() {
+        return instance;
+    }
+
+    /**
+     * Clear the model.
+     */
+    public synchronized void clear() {
+        stackPaths.clear();
+        globalStats.clear();
+        stacks.clear();
+    }
+
+    /**
+     *
+     * @return the stack paths
+     */
+    public MemAllocProfilerPaths getStackPaths() {
+        return stackPaths;
+    }
+
+    /**
+     *
+     * @param id the id of the allocation entry
+     * @return the allocation entry
+     */
+    public Entry getEntry(int id) {
+        return stackPaths.getEntry(id);
+    }
+
+    /**
+     * @return the global (overall) allocations statistic
+     */
+    public Stats getGlobalStats() {
+        return globalStats;
+    }
+
+    void push(String name, SourceSection section) {
+        Deque<Entry> stack = getStackForThread();
+        Entry parentEntry = stack.peek();
+        Entry entry = parentEntry.children.get(section);
+        if (entry == null) {
+            entry = new Entry(stackPaths, parentEntry, name, section);
+        }
+        stack.push(entry);
+    }
+
+    private Deque<Entry> getStackForThread() {
+        return stacks.computeIfAbsent(Thread.currentThread(), (t) -> {
+            ConcurrentLinkedDeque<Entry> stk = new ConcurrentLinkedDeque<>();
+            Entry stackRoot = new Entry(stackPaths, stackPaths.getRootEntry(), "<" + Thread.currentThread().getName() + ">", null);
+            stk.add(stackRoot);
+            return stk;
+        });
+    }
+
+    void pop() {
+        Deque<Entry> stack = stacks.get(Thread.currentThread());
+        if (stack != null) {
+            // The stacks map might get cleared during the preceding execution of the instrumented
+            // node or its child node. Typically it happens if there is a function in a guest
+            // language turning off the profiler.
+            stack.pop();
+        }
+    }
+
+    void reportAllocation(long size) {
+        Deque<Entry> stack = stacks.get(Thread.currentThread());
+        if (stack != null) {
+            Entry entry = stack.peek();
+            globalStats.add(size, 1);
+            entry.stats.add(size, 1);
+        }
+    }
+
+    public static final class AlocatedAggrComparator implements Comparator<Entry> {
+
+        private final boolean desc;
+
+        public AlocatedAggrComparator(boolean desc) {
+            this.desc = desc;
+        }
+
+        @Override
+        public int compare(Entry e1, Entry e2) {
+            Long a1 = e1.getAllocatedAggr();
+            Long a2 = e2.getAllocatedAggr();
+            return desc ? a2.compareTo(a1) : a1.compareTo(a2);
+        }
+    }
+}
diff --git a/documentation/allocprof.md b/documentation/allocprof.md
new file mode 100644
index 0000000000..3358da8b77
--- /dev/null
+++ b/documentation/allocprof.md
@@ -0,0 +1,83 @@
+# FastR Allocation Profiler - Getting Started 
+
+The basic usage is illustrated by the following examples:
+
+* Activating the profiling instrument
+```
+.fastr.profmem(TRUE)
+```
+
+* Actual profiling 
+```
+example(glm)
+```
+* Creating a snapshot
+```
+s1 <- .fastr.profmem.snapshot("s1")
+```
+* Displaying the result
+```
+.fastr.profmem.show(snapshot=s1)
+```
+producing:
+```
+/<main>[id=0] { size: 35155856, count: 140515 }
+/<main>[id=0]/example[id=2788] { size: 17197400, count: 87422 }
+/<main>[id=0]/example[id=2788]/source[id=2987] { size: 16747568, count: 82327 }
+/<main>[id=0]/example[id=2788]/source[id=2987]/source[id=3107] { size: 14999312, count: 76365 }
+/<main>[id=0]/example[id=2788]/source[id=2987]/source[id=3107]/source[id=3108] { size: 10815880, count: 56889 }
+/<main>[id=0]/example[id=2788]/source[id=2987]/source[id=3107]/source[id=3108]/eval[id=3109] { size: 10784176, count: 56823 }
+/<main>[id=0]/example[id=2788]/source[id=2987]/source[id=3107]/source[id=3108]/eval[id=3109]/<no source>[id=8056] { size: 4317704, count: 684 }
+/<main>[id=0]/example[id=2788]/source[id=2987]/source[id=3107]/source[id=3108]/eval[id=3109]/<no source>[id=8056]/<no source>[id=8058] { size: 4317704, count: 684 }
+```
+
+* Displaying the result, 3 top levels only
+```
+.fastr.profmem.show(3, snapshot=s1)
+```
+producing:
+```
+/<main>[id=0] { size: 35140384, count: 140294 }
+/<main>[id=0]/example[id=2788] { size: 17197400, count: 87422 }
+/<main>[id=0]/example[id=2788]/source[id=2987] { size: 16747568, count: 82327 }
+/<main>[id=0]/example[id=2788]/source[id=2803] { size: 292256, count: 3114 }
+/<main>[id=0]/example[id=2788]/source[id=2969] { size: 5456, count: 47 }
+/<main>[id=0]/example[id=2788]/source[id=2978] { size: 3024, count: 26 }
+/<main>[id=0]/example[id=2788]/source[id=2789] { size: 824, count: 15 }
+/<main>[id=0]/example[id=2788]/source[id=2796] { size: 112, count: 2 }
+/<main>[id=0]/example[id=2788]/source[id=2973] { size: 88, count: 2 }
+/<main>[id=0]/example[id=2788]/source[id=2986] { size: 56, count: 1 }
+/<main>[id=0]/example[id=2788]/source[id=2795] { size: 32, count: 1 }
+/<main>[id=0]/example[id=2788]/source[id=2977] { size: 0, count: 0 }
+```
+* Showing the source associated with a stack entry
+```
+# stack entry id = 2630
+.fastr.profmem.source(2630, snapshot=s1)
+```
+producing:
+```
+<<<  at 53:9
+txt <- unlist(x)
+>>>  at 53:24
+```
+* Showing the hot-spots
+```
+# single-level hospot view
+.fastr.profmem.show(2, snapshot=s1, view = "hotspots")
+# unlimited hostspot view
+.fastr.profmem.show(snapshot=s1, view = "hotspots")
+# show the source of the entry 2630 from the hostspot view on the snapshot s1
+.fastr.profmem.source(2630, view="hotspots", snapshot=s1)
+```
+* Deactivating the profilinig instrument
+```
+.fastr.profmem(FALSE)
+```
+
+The `.fastr.profmem.show` builtin offers several arguments for controlling the output:
+
+* `levels`: determines the maximum number of levels displayed
+* `desc`: specifies the sorting order, which is TRUE by default, ie. descending
+* `id`: displays only the sub tree of the specified stack entry
+* `printParents`: forces printing the parent stack entries. It is meaningful in connection with a nun-NULL id parameter value only.
\ No newline at end of file
-- 
GitLab