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 ac7b085011344fd31ae0976a6ef408bea7c916fd..d03f663b1151eaae9a39bdecede4f62e9996a80e 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,9 +50,9 @@ 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.context.RprofState;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
@@ -64,7 +64,7 @@ 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().stateRprof;
+        RprofState profState = RContext.getInstance().stateInstrumentation.getRprof();
         String filename = filenameVec.getDataAt(0);
         if (filename.length() == 0) {
             // disable
@@ -100,7 +100,7 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
     }
 
     private static void endProfiling() {
-        RprofState profState = RContext.getInstance().stateRprof;
+        RprofState profState = RContext.getInstance().stateInstrumentation.getRprof();
         ProfileThread profileThread = (ProfileThread) profState.profileThread();
         profileThread.running = false;
         HashMap<String, Integer> fileMap = null;
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 042b3d0cca597784dde0e0c3c4030d1a64fe0d79..d0e3a8c0b290f4475b190a19221974a6ec1ebd32 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
@@ -44,14 +44,12 @@ import com.oracle.truffle.r.nodes.builtin.base.foreign.DotC;
 import com.oracle.truffle.r.nodes.builtin.base.foreign.DotCNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.foreign.ForeignFunctions;
 import com.oracle.truffle.r.nodes.builtin.base.foreign.ForeignFunctionsFactory;
-import com.oracle.truffle.r.nodes.builtin.fastr.FastRCallCounting;
-import com.oracle.truffle.r.nodes.builtin.fastr.FastRCallCountingFactory;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRContext;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRContextFactory;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRDebug;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRDebugNodeGen;
-import com.oracle.truffle.r.nodes.builtin.fastr.FastRFunctionTimer;
-import com.oracle.truffle.r.nodes.builtin.fastr.FastRFunctionTimerFactory;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRFunctionProfiler;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRFunctionProfilerFactory;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRIdentity;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRIdentityNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRInspect;
@@ -277,8 +275,6 @@ public class BasePackage extends RBuiltinPackage {
         add(WithVisible.class, WithVisibleNodeGen::create);
         add(Exists.class, ExistsNodeGen::create);
         add(Expression.class, ExpressionNodeGen::create);
-        add(FastRCallCounting.CreateCallCounter.class, FastRCallCountingFactory.CreateCallCounterNodeGen::create);
-        add(FastRCallCounting.GetCallCounter.class, FastRCallCountingFactory.GetCallCounterNodeGen::create);
         add(FastRContext.CloseChannel.class, FastRContextFactory.CloseChannelNodeGen::create);
         add(FastRContext.Create.class, FastRContextFactory.CreateNodeGen::create);
         add(FastRContext.CreateChannel.class, FastRContextFactory.CreateChannelNodeGen::create);
@@ -293,8 +289,10 @@ public class BasePackage extends RBuiltinPackage {
         add(FastRContext.Join.class, FastRContextFactory.JoinNodeGen::create);
         add(FastrDqrls.class, FastrDqrlsNodeGen::create);
         add(FastRDebug.class, FastRDebugNodeGen::create);
-        add(FastRFunctionTimer.CreateFunctionTimer.class, FastRFunctionTimerFactory.CreateFunctionTimerNodeGen::create);
-        add(FastRFunctionTimer.GetFunctionTimer.class, FastRFunctionTimerFactory.GetFunctionTimerNodeGen::create);
+        add(FastRFunctionProfiler.Create.class, FastRFunctionProfilerFactory.CreateNodeGen::create);
+        add(FastRFunctionProfiler.Get.class, FastRFunctionProfilerFactory.GetNodeGen::create);
+        add(FastRFunctionProfiler.Reset.class, FastRFunctionProfilerFactory.ResetNodeGen::create);
+        add(FastRFunctionProfiler.Clear.class, FastRFunctionProfilerFactory.ClearNodeGen::create);
         add(FastRIdentity.class, FastRIdentityNodeGen::create);
         add(FastRInspect.class, FastRInspectNodeGen::create);
         add(FastRInterop.Eval.class, FastRInteropFactory.EvalNodeGen::create);
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 45ded5c9169373e32d0615ac29f2fa4c9e7c9df0..341a599ab3100966d00a8cae5e0d361e6d1cf630 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
@@ -97,10 +97,10 @@ public class TraceFunctions {
         @Specialization
         @TruffleBoundary
         protected byte traceOnOff(byte state) {
-            boolean prevState = RContext.getInstance().stateTraceHandling.getTracingState();
+            boolean prevState = RContext.getInstance().stateInstrumentation.getTracingState();
             boolean newState = RRuntime.fromLogical(state);
             if (newState != prevState) {
-                RContext.getInstance().stateTraceHandling.setTracingState(newState);
+                RContext.getInstance().stateInstrumentation.setTracingState(newState);
             }
             return RRuntime.asLogical(prevState);
         }
@@ -108,7 +108,7 @@ public class TraceFunctions {
         @Specialization
         @TruffleBoundary
         protected byte traceOnOff(@SuppressWarnings("unused") RNull state) {
-            return RRuntime.asLogical(RContext.getInstance().stateTraceHandling.getTracingState());
+            return RRuntime.asLogical(RContext.getInstance().stateInstrumentation.getTracingState());
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRCallCounting.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRCallCounting.java
deleted file mode 100644
index 4039faa429afd3aafb46e1409bba4c31c8469d4f..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRCallCounting.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.nodes.builtin.fastr;
-
-import 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.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.instrumentation.REntryCounters;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RNull;
-
-public class FastRCallCounting {
-
-    @RBuiltin(name = ".fastr.createcc", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func"})
-    public abstract static class CreateCallCounter extends RBuiltinNode {
-        @Specialization
-        @TruffleBoundary
-        protected RNull createCallCounter(RFunction function) {
-            if (!function.isBuiltin()) {
-                REntryCounters.FunctionListener.installCounter(function);
-            }
-            return RNull.instance;
-        }
-
-        @SuppressWarnings("unused")
-        @Fallback
-        protected Object fallback(Object a1) {
-            throw RError.error(this, RError.Message.INVALID_ARGUMENT, "func");
-        }
-    }
-
-    @RBuiltin(name = ".fastr.getcc", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func"})
-    public abstract static class GetCallCounter extends RBuiltinNode {
-        @Specialization
-        @TruffleBoundary
-        protected Object getCallCount(RFunction function) {
-            if (!function.isBuiltin()) {
-                int entryCount = REntryCounters.FunctionListener.findCounter(function).getEnterCount();
-                if (entryCount < 0) {
-                    throw RError.error(this, RError.Message.GENERIC, "no associated counter");
-                } else {
-                    return entryCount;
-                }
-            }
-            return RNull.instance;
-        }
-
-        @SuppressWarnings("unused")
-        @Fallback
-        protected Object fallback(Object a1) {
-            throw RError.error(this, RError.Message.INVALID_ARGUMENT, "func");
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionProfiler.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionProfiler.java
new file mode 100644
index 0000000000000000000000000000000000000000..977de8fc70a5c51aefd9b21f23c0ab68316a6c81
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionProfiler.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.builtin.fastr;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+
+import java.util.ArrayList;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.missingValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.instrumentation.RFunctionProfiler;
+import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RDoubleVector;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RMissing;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.tools.Profiler;
+
+public class FastRFunctionProfiler {
+
+    @RBuiltin(name = ".fastr.profiler.create", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func", "mode"})
+    public abstract static class Create extends RBuiltinNode {
+        private static final int COUNTING = 1;
+        private static final int TIMING = 2;
+
+        @Override
+        public Object[] getDefaultParameterValues() {
+            return new Object[]{RMissing.instance, "counting"};
+        }
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("func").mustBe(instanceOf(RFunction.class).or(missingValue()));
+
+            casts.arg("mode").mustBe(stringValue()).asStringVector();
+        }
+
+        private int checkMode(RAbstractStringVector modeVec) {
+            int result = 0;
+            for (int i = 0; i < modeVec.getLength(); i++) {
+                String mode = modeVec.getDataAt(i);
+                switch (mode) {
+                    case "counting":
+                        result |= COUNTING;
+                        break;
+                    case "timing":
+                        result |= TIMING;
+                        break;
+                    default:
+                        throw RError.error(this, RError.Message.GENERIC, "invalid 'mode', one of 'count, timning' expected");
+                }
+            }
+            return result;
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected RNull createFunctionProfiler(RFunction function, RAbstractStringVector modeVec) {
+            int mode = checkMode(modeVec);
+            if (!function.isBuiltin()) {
+                RFunctionProfiler.installTimer(function, (mode & COUNTING) != 0, (mode & TIMING) != 0);
+            } else {
+                throw RError.error(this, RError.Message.GENERIC, "cannot profile builtin functions");
+            }
+            return RNull.instance;
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected RNull createFunctionProfiler(@SuppressWarnings("unused") RMissing value, RAbstractStringVector modeVec) {
+            int mode = checkMode(modeVec);
+            RFunctionProfiler.installTimer(null, (mode & COUNTING) != 0, (mode & TIMING) != 0);
+            return RNull.instance;
+        }
+
+    }
+
+    @RBuiltin(name = ".fastr.profiler.get", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func", "threshold", "scale"})
+    public abstract static class Get extends RBuiltinNode {
+
+        private static final RStringVector COLNAMES = RDataFactory.createStringVector(new String[]{"Invocations", "TotalTime", "SelfTime"}, RDataFactory.COMPLETE_VECTOR);
+        private static final RStringVector ROWNAMES = RDataFactory.createStringVector(new String[]{"Combined", "Interpreted", "Compiled"}, RDataFactory.COMPLETE_VECTOR);
+        private static final int NCOLS = 3;
+        private static final int NROWS = 3;
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("func").mustBe(instanceOf(RFunction.class).or(missingValue()));
+
+            casts.arg("threshold").mustBe(integerValue().or(doubleValue())).asDoubleVector();
+
+            casts.arg("scale").mustBe(stringValue()).asStringVector();
+        }
+
+        @Override
+        public Object[] getDefaultParameterValues() {
+            return new Object[]{RMissing.instance, 0.0, "nanos"};
+        }
+
+        private void checkScale(String s) throws RError {
+            if (!(s.equals("nanos") || s.equals("millis") || s.equals("micros") || s.equals("secs"))) {
+                throw RError.error(this, RError.Message.GENERIC, "invalid scale: one of 'nanos, micros, millis, secs' expected");
+            }
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected Object get(@SuppressWarnings("unused") RMissing value, RAbstractDoubleVector thresholdVec, RAbstractStringVector scaleVec) {
+            String scale = scaleVec.getDataAt(0);
+            checkScale(scale);
+            double threshold = thresholdVec.getDataAt(0);
+            Profiler.Counter[] counters = RFunctionProfiler.getCounters();
+            if (counters == null) {
+                throw RError.error(this, RError.Message.GENERIC, "profiling not enabled");
+            }
+            ArrayList<RDoubleVector> dataList = new ArrayList<>();
+            ArrayList<String> nameList = new ArrayList<>();
+            for (int i = 0; i < counters.length; i++) {
+                Profiler.Counter counter = counters[i];
+                if (threshold > 0.0) {
+                    long time = counter.getTotalTime(Profiler.Counter.TimeKind.INTERPRETED_AND_COMPILED);
+                    if (time <= threshold) {
+                        continue;
+                    }
+                }
+                dataList.add(getFunctionMatrix(counter, scale));
+                nameList.add(counter.getName());
+            }
+            Object[] data = new Object[dataList.size()];
+            String[] names = new String[nameList.size()];
+            return RDataFactory.createList(dataList.toArray(data), RDataFactory.createStringVector(nameList.toArray(names), RDataFactory.COMPLETE_VECTOR));
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected Object get(RFunction function, @SuppressWarnings("unused") RAbstractDoubleVector threshold, RAbstractStringVector scaleVec) {
+            String scale = scaleVec.getDataAt(0);
+            checkScale(scale);
+            if (!function.isBuiltin()) {
+                Profiler.Counter counter = RFunctionProfiler.getCounter(function);
+                if (counter == null) {
+                    throw RError.error(this, RError.Message.GENERIC, "profiling not enabled");
+                } else {
+                    return getFunctionMatrix(counter, scale);
+                }
+            } else {
+                throw RError.error(this, RError.Message.GENERIC, "cannot profile builtin functions");
+            }
+        }
+
+        private static RDoubleVector getFunctionMatrix(Profiler.Counter counter, String scale) {
+            double[] data = new double[NROWS * NCOLS];
+            boolean isTiming = RFunctionProfiler.isTiming();
+            boolean complete = isTiming ? RDataFactory.COMPLETE_VECTOR : RDataFactory.INCOMPLETE_VECTOR;
+            for (int r = 0; r < NROWS; r++) {
+                Profiler.Counter.TimeKind timeKind = Profiler.Counter.TimeKind.values()[r];
+                for (int c = 0; c < NCOLS; c++) {
+                    int index = c * NROWS + r;
+                    double value = 0.0;
+                    switch (c) {
+                        case 0:
+                            value = counter.getInvocations(timeKind);
+                            break;
+                        case 1:
+                            value = isTiming ? counter.getTotalTime(timeKind) : RRuntime.DOUBLE_NA;
+                            break;
+                        case 2:
+                            value = isTiming ? counter.getSelfTime(timeKind) : RRuntime.DOUBLE_NA;
+                    }
+                    data[index] = c == 0 ? value : scaledTime(scale, value);
+                }
+            }
+            RDoubleVector result = RDataFactory.createDoubleVector(data, complete, new int[]{3, 3});
+            Object[] dimNamesData = new Object[2];
+            dimNamesData[0] = ROWNAMES;
+            dimNamesData[1] = COLNAMES;
+            RList dimNames = RDataFactory.createList(dimNamesData);
+            result.setDimNames(dimNames);
+            return result;
+        }
+
+        private static double scaledTime(String scale, double time) {
+            if (RRuntime.isNA(time)) {
+                return time;
+            }
+            switch (scale) {
+                case "nanos":
+                    return time;
+                case "micros":
+                    return time / 1000.0;
+                case "millis":
+                    return time / 1000000.0;
+                case "secs":
+                    return time / 1000000000.0;
+                default:
+                    throw RInternalError.shouldNotReachHere();
+            }
+        }
+    }
+
+    @RBuiltin(name = ".fastr.profiler.reset", kind = RBuiltinKind.PRIMITIVE, parameterNames = {}, visibility = RVisibility.OFF)
+    public abstract static class Reset extends RBuiltinNode {
+        @Specialization
+        @TruffleBoundary
+        protected Object reset() {
+            RFunctionProfiler.reset();
+            return RNull.instance;
+        }
+    }
+
+    @RBuiltin(name = ".fastr.profiler.clear", kind = RBuiltinKind.PRIMITIVE, parameterNames = {}, visibility = RVisibility.OFF)
+    public abstract static class Clear extends RBuiltinNode {
+        @Specialization
+        @TruffleBoundary
+        protected Object clear() {
+            RFunctionProfiler.clear();
+            return RNull.instance;
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionTimer.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionTimer.java
deleted file mode 100644
index 1a967545babb8328c37243b0ba4d3f4370188406..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionTimer.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.nodes.builtin.fastr;
-
-import 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.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.instrumentation.RNodeTimer;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-
-public class FastRFunctionTimer {
-
-    @RBuiltin(name = ".fastr.createtimer", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func"})
-    public abstract static class CreateFunctionTimer extends RBuiltinNode {
-        @Specialization
-        @TruffleBoundary
-        protected RNull createFunctionTimer(RFunction function) {
-            if (!function.isBuiltin()) {
-                RNodeTimer.StatementListener.installTimer(function);
-            }
-            return RNull.instance;
-        }
-
-        @SuppressWarnings("unused")
-        @Fallback
-        protected Object fallback(Object a1) {
-            throw RError.error(this, RError.Message.INVALID_ARGUMENT, "func");
-        }
-    }
-
-    @RBuiltin(name = ".fastr.gettimer", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func", "scale"})
-    public abstract static class GetFunctionTimer extends RBuiltinNode {
-        @Override
-        public Object[] getDefaultParameterValues() {
-            return new Object[]{RMissing.instance, "nanos"};
-        }
-
-        @Specialization
-        @TruffleBoundary
-        protected Object getFunctionTimer(RFunction function, RAbstractStringVector scale) {
-            if (!function.isBuiltin()) {
-                long timeInfo = RNodeTimer.StatementListener.findTimer(function);
-                if (timeInfo < 0) {
-                    throw RError.error(this, RError.Message.GENERIC, "no associated timer");
-                } else {
-                    double timeVal = timeInfo;
-                    switch (scale.getDataAt(0)) {
-                        case "nanos":
-                            break;
-                        case "micros":
-                            timeVal = timeVal / 1000.0;
-                            break;
-                        case "millis":
-                            timeVal = timeVal / 1000000.0;
-                            break;
-                        case "secs":
-                            timeVal = timeVal / 1000000000.0;
-                            break;
-                        default:
-                            throw RError.error(this, RError.Message.GENERIC, "invalid scale: one of 'nanos, micros, millis, secs' expected");
-                    }
-                    return RDataFactory.createDoubleVectorFromScalar(timeVal);
-                }
-            }
-            return RNull.instance;
-        }
-
-        @SuppressWarnings("unused")
-        @Fallback
-        protected Object fallback(Object a1, Object a2) {
-            throw RError.error(this, RError.Message.INVALID_ARGUMENT, "func");
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
index 0e04ac5e551ba5ac58f805bea5dae3e864e09e79..76c6e1167e48c003fdb5ee32c9fb597514912321 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
@@ -24,7 +24,6 @@ package com.oracle.truffle.r.nodes.builtin.helpers;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.WeakHashMap;
 
 import com.oracle.truffle.api.Assumption;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -44,7 +43,6 @@ import com.oracle.truffle.r.nodes.control.AbstractLoopNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
 import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags;
-import com.oracle.truffle.r.runtime.FunctionUID;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
@@ -102,11 +100,6 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNodeVisitor;
  */
 public class DebugHandling {
 
-    /**
-     * Records all functions that have debug listeners installed.
-     */
-    private static final WeakHashMap<FunctionUID, FunctionStatementsEventListener> listenerMap = new WeakHashMap<>();
-
     /**
      * This flag is used to (temporarily) disable all debugging across calls that are used
      * internally in the implementation.
@@ -117,8 +110,7 @@ public class DebugHandling {
      * Attach the DebugHandling instrument to the FunctionStatementsNode and all syntactic nodes.
      */
     public static boolean enableDebug(RFunction func, Object text, Object condition, boolean once) {
-        FunctionDefinitionNode fdn = (FunctionDefinitionNode) func.getRootNode();
-        FunctionStatementsEventListener fbr = listenerMap.get(fdn.getUID());
+        FunctionStatementsEventListener fbr = getFunctionStatementsEventListener(func);
         if (fbr == null) {
             attachDebugHandler(func, text, condition, once);
         } else {
@@ -128,7 +120,7 @@ public class DebugHandling {
     }
 
     public static boolean undebug(RFunction func) {
-        FunctionStatementsEventListener fbr = listenerMap.get(((FunctionDefinitionNode) func.getRootNode()).getUID());
+        FunctionStatementsEventListener fbr = getFunctionStatementsEventListener(func);
         if (fbr == null) {
             return false;
         } else {
@@ -138,10 +130,18 @@ public class DebugHandling {
     }
 
     public static boolean isDebugged(RFunction func) {
-        FunctionStatementsEventListener fser = listenerMap.get(((FunctionDefinitionNode) func.getRootNode()).getUID());
+        FunctionStatementsEventListener fser = getFunctionStatementsEventListener(func);
         return fser != null && !fser.disabled();
     }
 
+    private static FunctionStatementsEventListener getFunctionStatementsEventListener(RFunction func) {
+        return (FunctionStatementsEventListener) RContext.getInstance().stateInstrumentation.getDebugListener(RInstrumentation.getSourceSection(func));
+    }
+
+    private static FunctionStatementsEventListener getFunctionStatementsEventListener(FunctionDefinitionNode fdn) {
+        return (FunctionStatementsEventListener) RContext.getInstance().stateInstrumentation.getDebugListener(fdn.getSourceSection());
+    }
+
     /**
      * Disables/enables debugging globally. Intended to be used for short period, typically while
      * executing functions used internally by the implementation.
@@ -186,7 +186,7 @@ public class DebugHandling {
     }
 
     private static void ensureSingleStep(FunctionDefinitionNode fdn) {
-        FunctionStatementsEventListener fser = listenerMap.get(fdn.getUID());
+        FunctionStatementsEventListener fser = getFunctionStatementsEventListener(fdn);
         if (fser == null) {
             // attach a "once" listener
             fser = attachDebugHandler(fdn, null, null, true);
@@ -262,7 +262,7 @@ public class DebugHandling {
                          * will everything get invalidated?
                          */
                         stepIntoInstrument = RInstrumentation.getInstrumenter().attachListener(SourceSectionFilter.newBuilder().tagIs(StandardTags.RootTag.class).build(),
-                                        new StepIntoInstrumentListener(listenerMap.get(functionDefinitionNode.getUID())));
+                                        new StepIntoInstrumentListener(getFunctionStatementsEventListener(functionDefinitionNode)));
                     }
                     break;
                 case BrowserInteractNode.CONTINUE:
@@ -275,7 +275,7 @@ public class DebugHandling {
                     AbstractLoopNode loopNode = inLoop(node);
                     if (loopNode != null) {
                         // Have to disable just the body of the loop
-                        FunctionStatementsEventListener fser = listenerMap.get(functionDefinitionNode.getUID());
+                        FunctionStatementsEventListener fser = getFunctionStatementsEventListener(functionDefinitionNode);
                         fser.setFinishing(loopNode);
                     } else {
                         doContinue();
@@ -285,7 +285,7 @@ public class DebugHandling {
         }
 
         private void doContinue() {
-            FunctionStatementsEventListener fser = listenerMap.get(functionDefinitionNode.getUID());
+            FunctionStatementsEventListener fser = getFunctionStatementsEventListener(functionDefinitionNode);
             fser.setContinuing();
         }
 
@@ -323,7 +323,7 @@ public class DebugHandling {
 
         FunctionStatementsEventListener(FunctionDefinitionNode functionDefinitionNode, Object text, Object condition, boolean once) {
             super(functionDefinitionNode, text, condition);
-            listenerMap.put(functionDefinitionNode.getUID(), this);
+            RContext.getInstance().stateInstrumentation.putDebugListener(functionDefinitionNode.getSourceSection(), this);
             statementListener = new StatementEventListener(functionDefinitionNode, text, condition);
             this.once = once;
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
index 1418ab39e90504bb45fd8867c7bec30ed9562d02..798fd225d0eb4101e79b8ed1b90b3722c0577653 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
@@ -33,7 +33,6 @@ import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
 import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
 import com.oracle.truffle.api.instrumentation.StandardTags;
 import com.oracle.truffle.api.utilities.CyclicAssumption;
-import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RArguments;
@@ -52,9 +51,8 @@ import com.oracle.truffle.r.runtime.data.RMissing;
 public class TraceHandling {
 
     public static void enableTrace(RFunction func) {
-        FunctionDefinitionNode fdn = (FunctionDefinitionNode) func.getRootNode();
         @SuppressWarnings("unchecked")
-        EventBinding<TraceEventListener> binding = (EventBinding<TraceEventListener>) RContext.getInstance().stateTraceHandling.get(fdn.getUID());
+        EventBinding<TraceEventListener> binding = (EventBinding<TraceEventListener>) RContext.getInstance().stateInstrumentation.getTraceBinding(RInstrumentation.getSourceSection(func));
         if (binding != null) {
             // only one
             binding.dispose();
@@ -63,17 +61,16 @@ public class TraceHandling {
     }
 
     public static void disableTrace(RFunction func) {
-        FunctionDefinitionNode fdn = (FunctionDefinitionNode) func.getRootNode();
         @SuppressWarnings("unchecked")
-        EventBinding<TraceEventListener> binding = (EventBinding<TraceEventListener>) RContext.getInstance().stateTraceHandling.get(fdn.getUID());
+        EventBinding<TraceEventListener> binding = (EventBinding<TraceEventListener>) RContext.getInstance().stateInstrumentation.getTraceBinding(RInstrumentation.getSourceSection(func));
         if (binding != null) {
             binding.dispose();
-            RContext.getInstance().stateTraceHandling.put(RInstrumentation.getFunctionDefinitionNode(func).getUID(), null);
+            RContext.getInstance().stateInstrumentation.putTraceBinding(RInstrumentation.getSourceSection(func), null);
         }
     }
 
     public static void setTracingState(boolean state) {
-        Object[] listeners = RContext.getInstance().stateTraceHandling.getListeners();
+        EventBinding<?>[] listeners = RContext.getInstance().stateInstrumentation.getTraceBindings();
         for (int i = 0; i < listeners.length; i++) {
             @SuppressWarnings("unchecked")
             EventBinding<TraceEventListener> binding = (EventBinding<TraceEventListener>) listeners[i];
@@ -97,9 +94,8 @@ public class TraceHandling {
     }
 
     public static boolean enableStatementTrace(RFunction func, RLanguage tracer, @SuppressWarnings("unused") Object exit, Object at, boolean print) {
-        FunctionDefinitionNode fdn = (FunctionDefinitionNode) func.getRootNode();
         @SuppressWarnings("unchecked")
-        EventBinding<TraceEventListener> binding = (EventBinding<TraceEventListener>) RContext.getInstance().stateTraceHandling.get(fdn.getUID());
+        EventBinding<TraceEventListener> binding = (EventBinding<TraceEventListener>) RContext.getInstance().stateInstrumentation.getTraceBinding(RInstrumentation.getSourceSection(func));
         if (binding != null) {
             // only one allowed
             binding.dispose();
@@ -109,7 +105,7 @@ public class TraceHandling {
             TracerFunctionEntryEventListener listener = new TracerFunctionEntryEventListener(tracer, print);
             binding = RInstrumentation.getInstrumenter().attachListener(RInstrumentation.createFunctionStartFilter(func).build(), listener);
             setOutputHandler();
-            RContext.getInstance().stateTraceHandling.put(RInstrumentation.getFunctionDefinitionNode(func).getUID(), binding);
+            RContext.getInstance().stateInstrumentation.putTraceBinding(RInstrumentation.getSourceSection(func), binding);
         }
         return false;
     }
@@ -118,7 +114,7 @@ public class TraceHandling {
         PrimitiveFunctionEntryEventListener fser = new PrimitiveFunctionEntryEventListener();
         EventBinding<TraceEventListener> binding = RInstrumentation.getInstrumenter().attachListener(RInstrumentation.createFunctionStartFilter(func).build(), fser);
         setOutputHandler();
-        RContext.getInstance().stateTraceHandling.put(RInstrumentation.getFunctionDefinitionNode(func).getUID(), binding);
+        RContext.getInstance().stateInstrumentation.putTraceBinding(RInstrumentation.getSourceSection(func), binding);
     }
 
     private abstract static class TraceEventListener implements ExecutionEventListener {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ALONGFunctionUIDFactory.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ALONGFunctionUIDFactory.java
deleted file mode 100644
index f508505dfa5eb8ec196ebf574896c10b90ef6340..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ALONGFunctionUIDFactory.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.nodes.function;
-
-import java.util.concurrent.atomic.AtomicLong;
-
-import com.oracle.truffle.r.runtime.FunctionUID;
-import com.oracle.truffle.r.runtime.instrument.FunctionUIDFactory;
-
-public class ALONGFunctionUIDFactory extends FunctionUIDFactory {
-
-    private static final AtomicLong ID = new AtomicLong();
-
-    private static final class ALongFunctionUID implements FunctionUID {
-
-        private final long uuid;
-
-        private ALongFunctionUID(long uuid) {
-            this.uuid = uuid;
-        }
-
-        @Override
-        public int compareTo(FunctionUID o) {
-            ALongFunctionUID oa = (ALongFunctionUID) o;
-            if (uuid == oa.uuid) {
-                return 0;
-            } else if (uuid < oa.uuid) {
-                return -1;
-            } else {
-                return 1;
-            }
-        }
-
-        @Override
-        public String toString() {
-            return Long.toString(uuid);
-        }
-    }
-
-    @Override
-    public FunctionUID createUID() {
-        return new ALongFunctionUID(ID.incrementAndGet());
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
index 4433a0eae310957e59d41789a6ff8864618845ea..9e3d611808fd439720b539a5e27f79e9e7889082 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
@@ -47,10 +47,8 @@ import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinFactory;
 import com.oracle.truffle.r.nodes.control.BreakException;
 import com.oracle.truffle.r.nodes.control.NextException;
-import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.BrowserQuitException;
-import com.oracle.truffle.r.runtime.FunctionUID;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
@@ -63,21 +61,19 @@ import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.ReturnException;
 import com.oracle.truffle.r.runtime.Utils.DebugExitException;
-import com.oracle.truffle.r.runtime.WithFunctionUID;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
-import com.oracle.truffle.r.runtime.instrument.FunctionUIDFactory;
 import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxFunction;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNode, WithFunctionUID, RSyntaxFunction {
+public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNode, RSyntaxFunction {
 
     @Child private RNode body; // typed as RNode to avoid custom instrument wrapper
     /**
@@ -92,7 +88,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
      * loaded from packages, where at the point of definition any assignee variable is unknown.
      */
     private String name;
-    private FunctionUID uuid;
     private boolean instrumented = false;
     private SourceSection sourceSectionR;
     private final SourceSection[] argSourceSections;
@@ -133,11 +128,11 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
 
     public static FunctionDefinitionNode create(SourceSection src, FrameDescriptor frameDesc, SourceSection[] argSourceSections, SaveArgumentsNode saveArguments, RSyntaxNode body,
                     FormalArguments formals, String name, PostProcessArgumentsNode argPostProcess) {
-        return new FunctionDefinitionNode(src, frameDesc, argSourceSections, saveArguments, body, formals, name, argPostProcess, FunctionUIDFactory.get().createUID());
+        return new FunctionDefinitionNode(src, frameDesc, argSourceSections, saveArguments, body, formals, name, argPostProcess);
     }
 
     private FunctionDefinitionNode(SourceSection src, FrameDescriptor frameDesc, SourceSection[] argSourceSections, RNode saveArguments, RSyntaxNode body, FormalArguments formals,
-                    String name, PostProcessArgumentsNode argPostProcess, FunctionUID uuid) {
+                    String name, PostProcessArgumentsNode argPostProcess) {
         super(null, formals, frameDesc, RASTBuilder.createFunctionFastPath(body, formals.getSignature()));
         this.argSourceSections = argSourceSections;
         assert FrameSlotChangeMonitor.isValidFrameDescriptor(frameDesc);
@@ -147,11 +142,9 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         this.body = body.asRNode();
         this.name = name;
         this.onExitSlot = FrameSlotNode.createInitialized(frameDesc, RFrameSlot.OnExit, false);
-        this.uuid = uuid;
         this.needsSplitting = needsAnyBuiltinSplitting();
         this.containsDispatch = containsAnyDispatch(body);
         this.argPostProcess = argPostProcess;
-        RInstrumentation.registerFunctionDefinition(this);
     }
 
     @Override
@@ -165,7 +158,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
             args.add(RCodeBuilder.argument(source, getFormalArguments().getSignature().getName(i), value == null ? null : builder.process(value.asRSyntaxNode())));
         }
         RootCallTarget callTarget = RContext.getASTBuilder().rootFunction(getSourceSection(), args, builder.process(getBody()), name);
-        ((FunctionDefinitionNode) callTarget.getRootNode()).uuid = uuid;
         return callTarget;
     }
 
@@ -235,11 +227,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         return needsSplitting;
     }
 
-    @Override
-    public FunctionUID getUID() {
-        return uuid;
-    }
-
     public RSyntaxNode getBody() {
         return body.asRSyntaxNode();
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UUIDFunctionUIDFactory.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UUIDFunctionUIDFactory.java
deleted file mode 100644
index 3d3a4163f8cb8fb63f8b708b50f4f2cd93d20b84..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UUIDFunctionUIDFactory.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.nodes.function;
-
-import java.util.UUID;
-
-import com.oracle.truffle.r.runtime.FunctionUID;
-import com.oracle.truffle.r.runtime.instrument.FunctionUIDFactory;
-
-public class UUIDFunctionUIDFactory extends FunctionUIDFactory {
-
-    private static final class UUIDFunctionUID implements FunctionUID {
-
-        private final UUID uuid;
-
-        private UUIDFunctionUID(UUID uuid) {
-            this.uuid = uuid;
-        }
-
-        @Override
-        public int compareTo(FunctionUID o) {
-            return uuid.compareTo(((UUIDFunctionUID) o).uuid);
-        }
-
-        @Override
-        public String toString() {
-            return uuid.toString();
-        }
-    }
-
-    @Override
-    public FunctionUID createUID() {
-        return new UUIDFunctionUID(UUID.randomUUID());
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/REntryCounters.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/REntryCounters.java
deleted file mode 100644
index 33343ce29eb227fb13acccd8deb64c2440d0f20c..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/REntryCounters.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.nodes.instrumentation;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrumentation.EventContext;
-import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
-import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
-import com.oracle.truffle.api.instrumentation.StandardTags;
-import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
-import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation.FunctionIdentification;
-import com.oracle.truffle.r.runtime.FunctionUID;
-import com.oracle.truffle.r.runtime.RPerfStats;
-import com.oracle.truffle.r.runtime.data.RFunction;
-
-/**
- * Basic support for adding entry/exit counters to nodes. The {@link SourceSection} attribute is is
- * used to retrieve the counter associated with a node.
- *
- */
-public class REntryCounters {
-    public static final class Counter {
-        private final Object ident;
-        private int enterCount;
-        private int exitCount;
-
-        Counter(Object ident) {
-            this.ident = ident;
-        }
-
-        public int getEnterCount() {
-            return enterCount;
-        }
-
-        public int getExitCount() {
-            return exitCount;
-        }
-
-        public Object getIdent() {
-            return ident;
-        }
-    }
-
-    /**
-     * Listener that is independent of the kind of node and specific instance being counted.
-     */
-    private abstract static class BasicListener implements ExecutionEventListener {
-
-        private HashMap<SourceSection, Counter> counterMap = new HashMap<>();
-
-        private Counter getCounter(EventContext context) {
-            SourceSection ss = context.getInstrumentedSourceSection();
-            Counter counter = counterMap.get(ss);
-            if (counter == null) {
-                Object obj = counterCreated(context);
-                counter = new Counter(obj);
-                counterMap.put(ss, counter);
-            }
-            return counter;
-        }
-
-        protected Counter getCounter(SourceSection sourceSection) {
-            Counter counter = counterMap.get(sourceSection);
-            assert counter != null;
-            return counter;
-        }
-
-        protected Map<SourceSection, Counter> getCounterMap() {
-            return counterMap;
-        }
-
-        @Override
-        public void onEnter(EventContext context, VirtualFrame frame) {
-            getCounter(context).enterCount++;
-        }
-
-        @Override
-        public void onReturnValue(EventContext context, VirtualFrame frame, Object result) {
-            getCounter(context).exitCount++;
-        }
-
-        @Override
-        public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) {
-            getCounter(context).exitCount++;
-        }
-
-        protected abstract Object counterCreated(EventContext context);
-    }
-
-    /**
-     * A counter that is specialized for function entry, tagged with the {@link FunctionUID}.
-     */
-    public static class FunctionListener extends BasicListener {
-        private static final FunctionListener singleton = new FunctionListener();
-
-        static void installCounters() {
-            if (enabled()) {
-                SourceSectionFilter.Builder builder = SourceSectionFilter.newBuilder();
-                builder.tagIs(StandardTags.RootTag.class);
-                SourceSectionFilter filter = builder.build();
-                RInstrumentation.getInstrumenter().attachListener(filter, singleton);
-            }
-        }
-
-        public static void installCounter(RFunction func) {
-            RInstrumentation.getInstrumenter().attachListener(RInstrumentation.createFunctionStartFilter(func).build(), singleton);
-        }
-
-        public static Counter findCounter(RFunction func) {
-            FunctionDefinitionNode fdn = (FunctionDefinitionNode) func.getRootNode();
-            return singleton.getCounter(fdn.getBody().getSourceSection());
-        }
-
-        @Override
-        protected FunctionUID counterCreated(EventContext context) {
-            FunctionDefinitionNode fdn = (FunctionDefinitionNode) context.getInstrumentedNode().getRootNode();
-            return fdn.getUID();
-        }
-
-        static {
-            RPerfStats.register(new PerfHandler());
-        }
-
-        static boolean enabled() {
-            return RPerfStats.enabled(PerfHandler.NAME);
-        }
-
-        private static class PerfHandler implements RPerfStats.Handler {
-            private static class FunctionCount implements Comparable<FunctionCount> {
-                int count;
-                String name;
-
-                FunctionCount(int count, String name) {
-                    this.count = count;
-                    this.name = name;
-                }
-
-                @Override
-                public int compareTo(FunctionCount o) {
-                    if (count < o.count) {
-                        return 1;
-                    } else if (count > o.count) {
-                        return -1;
-                    } else {
-                        return name.compareTo(o.name);
-                    }
-                }
-            }
-
-            static final String NAME = "functioncounts";
-
-            @Override
-            public void initialize(String optionText) {
-            }
-
-            @Override
-            public String getName() {
-                return NAME;
-            }
-
-            /**
-             * R's anonymous function definitions don't help with reporting. We make an attempt to
-             * locate a function name in the global/package environments.
-             */
-            @Override
-            public void report() {
-                RPerfStats.out().println("R Function Entry Counts");
-                ArrayList<FunctionCount> results = new ArrayList<>();
-                for (Map.Entry<SourceSection, Counter> entry : FunctionListener.singleton.getCounterMap().entrySet()) {
-                    Counter counter = entry.getValue();
-                    FunctionIdentification fdi = RInstrumentation.getFunctionIdentification((FunctionUID) counter.getIdent());
-                    int count = counter.getEnterCount();
-                    if (count > 0) {
-                        results.add(new FunctionCount(count, fdi.name));
-                    }
-                }
-                FunctionCount[] sortedCounts = new FunctionCount[results.size()];
-                results.toArray(sortedCounts);
-                Arrays.sort(sortedCounts);
-                for (FunctionCount functionCount : sortedCounts) {
-                    RPerfStats.out().printf("%6d: %s%n", functionCount.count, functionCount.name);
-                }
-            }
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RFunctionProfiler.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RFunctionProfiler.java
new file mode 100644
index 0000000000000000000000000000000000000000..7689451ea34d03a7b9860deb0c67742a3272badd
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RFunctionProfiler.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.instrumentation;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.runtime.RPerfStats;
+import com.oracle.truffle.r.runtime.RSource;
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.tools.Profiler;
+import com.oracle.truffle.tools.Profiler.Counter;
+import com.oracle.truffle.tools.Profiler.Counter.TimeKind;
+
+/**
+ * Interface to the Truffle {@link Profiler}.
+ */
+public class RFunctionProfiler {
+    static {
+        RPerfStats.register(new PerfHandler());
+    }
+
+    static boolean enabled() {
+        return RPerfStats.enabled(PerfHandler.NAME);
+    }
+
+    /**
+     * This is called on startup to support {@link RPerfStats}.
+     */
+    static void installTimers(RContext context) {
+        if (enabled()) {
+            enableTiming(context, true, true);
+        }
+    }
+
+    private static Profiler getProfiler(RContext context) {
+        PolyglotEngine vm = context.getVM();
+        Profiler profiler = Profiler.find(vm);
+        return profiler;
+    }
+
+    private static Profiler getProfiler() {
+        PolyglotEngine vm = RContext.getInstance().getVM();
+        Profiler profiler = Profiler.find(vm);
+        return profiler;
+    }
+
+    private static void enableTiming(RContext context, @SuppressWarnings("unused") boolean counting, boolean timing) {
+        Profiler profiler = getProfiler(context);
+        context.getInstrumentationState().setProfiler(profiler);
+        profiler.setTiming(timing);
+        profiler.setCollecting(true);
+    }
+
+    /**
+     * (Interactively) installs a timer for a specific function. Currently the {@link Profiler} does
+     * not support profiling limited to specific functions so this effectively enables everything.
+     * If {@code func} is {@code null} profile all functions. In principle profiling can be
+     * restricted to entry counting and timing but currently counting is always on.
+     *
+     */
+    public static void installTimer(@SuppressWarnings("unused") RFunction func, boolean counting, boolean timing) {
+        enableTiming(RContext.getInstance(), counting, timing);
+    }
+
+    public static Counter getCounter(RFunction func) {
+        Profiler profiler = getProfiler();
+        if (profiler.isCollecting()) {
+            String funcName = func.getTarget().getRootNode().getName();
+            Map<SourceSection, Counter> counters = profiler.getCounters();
+            for (Counter counter : counters.values()) {
+                if (counter.getName().equals(funcName)) {
+                    return counter;
+                }
+            }
+        }
+        return null;
+    }
+
+    public static void reset() {
+        Profiler profiler = getProfiler();
+        profiler.clearData();
+        profiler.setCollecting(false);
+    }
+
+    public static void clear() {
+        Profiler profiler = getProfiler();
+        profiler.clearData();
+    }
+
+    public static Counter[] getCounters() {
+        Profiler profiler = getProfiler();
+        if (profiler.isCollecting()) {
+            Map<SourceSection, Counter> counters = profiler.getCounters();
+            Counter[] result = new Counter[counters.size()];
+            counters.values().toArray(result);
+            return result;
+        } else {
+            return null;
+        }
+    }
+
+    public static boolean isTiming() {
+        Profiler profiler = getProfiler();
+        return profiler.isTiming();
+    }
+
+    private static class PerfHandler implements RPerfStats.Handler {
+        static final String NAME = "timer";
+        @SuppressWarnings("unused") private boolean stmts;
+        private int threshold;
+
+        @Override
+        public void initialize(String optionText) {
+            if (optionText.length() > 0) {
+                String[] subOptions = optionText.split(":");
+                for (String subOption : subOptions) {
+                    if (subOption.equals("stmts")) {
+                        Utils.warn("statement timing is not implemented");
+                        stmts = true;
+                    } else if (subOption.startsWith("threshold")) {
+                        threshold = Integer.parseInt(subOption.substring(subOption.indexOf('=') + 1)) * 1000;
+                    }
+                }
+            }
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        private static class SortableCounter implements Comparable<SortableCounter> {
+            private Counter counter;
+
+            SortableCounter(Counter counter) {
+                this.counter = counter;
+            }
+
+            @Override
+            public int compareTo(SortableCounter other) {
+                long myTime = counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED);
+                long otherTime = other.counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED);
+                return myTime < otherTime ? 1 : (myTime > otherTime ? -1 : 0);
+            }
+
+        }
+
+        /**
+         * Report the statement timing information at the end of the run. The report is per function
+         * Functions that consumed less time than requested threshold (default 0) are not included
+         * in the report. The report is sorted by cumulative time.
+         */
+        @Override
+        public void report() {
+            Profiler profiler = RContext.getInstance().getInstrumentationState().getProfiler();
+            // profiler.printHistograms(RPerfStats.out());
+            Map<SourceSection, Counter> counters = profiler.getCounters();
+            long totalTime = 0;
+            SortableCounter[] sortedCounters = new SortableCounter[counters.size()];
+            int i = 0;
+            for (Counter counter : counters.values()) {
+                totalTime += counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED);
+                sortedCounters[i++] = new SortableCounter(counter);
+            }
+            Arrays.sort(sortedCounters);
+            for (SortableCounter scounter : sortedCounters) {
+                long time = scounter.counter.getSelfTime(TimeKind.INTERPRETED_AND_COMPILED);
+                if (time > 0 && time > threshold) {
+                    SourceSection ss = scounter.counter.getSourceSection();
+                    Source source = ss.getSource();
+                    RPerfStats.out().println("==========");
+                    double thisPercent = percent(time, totalTime);
+                    RPerfStats.out().printf("%d ms (%.2f%%): %s, %s%n", time, thisPercent, scounter.counter.getName(), RSource.getOrigin(source));
+                }
+            }
+            System.console();
+        }
+
+        private static double percent(long a, long b) {
+            return ((double) a * 100) / b;
+        }
+
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java
index 5bc62fc2332bc6d699e4e6a7cefe1f854990346d..c51da1dd105e9f7cda7fddaa74322a28564fa41c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java
@@ -22,142 +22,38 @@
  */
 package com.oracle.truffle.r.nodes.instrumentation;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 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.nodes.RootNode;
-import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.runtime.FastROptions;
-import com.oracle.truffle.r.runtime.FunctionUID;
-import com.oracle.truffle.r.runtime.RPerfStats;
-import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RPromise;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.env.REnvironment;
 
 /**
  * Handles the initialization of the (NEW) instrumentation system which sets up various instruments
- * depending on command line options.
+ * depending on command line options and provides utility methods for instrumentation-based tools.
  *
  */
 public class RInstrumentation {
 
-    /**
-     * Collects together all the relevant data for a function, keyed by the {@link FunctionUID},
-     * which is unique, for {@link RPerfStats} use.
-     */
-    private static Map<FunctionUID, FunctionData> functionMap;
-
-    /**
-     * Created lazily as needed.
-     */
-    static class FunctionIdentification {
-        public final Source source;
-        public final String name;
-        public final String origin;
-        public final FunctionDefinitionNode node;
-
-        FunctionIdentification(Source source, String name, String origin, FunctionDefinitionNode node) {
-            this.source = source;
-            this.name = name;
-            this.origin = origin;
-            this.node = node;
-        }
-    }
-
-    /**
-     * Created for every {@link FunctionDefinitionNode}. maybe could be lazier.
-     */
-    private static class FunctionData {
-        private final FunctionUID uid;
-        private final FunctionDefinitionNode fdn;
-        private FunctionIdentification ident;
-
-        FunctionData(FunctionUID uid, FunctionDefinitionNode fdn) {
-            this.uid = uid;
-            this.fdn = fdn;
-        }
-
-        private FunctionIdentification getIdentification() {
-            if (ident == null) {
-                SourceSection ss = fdn.getSourceSection();
-                /*
-                 * The default for "name" is the description associated with "fdn". If the function
-                 * was parsed from text this will be the variable name the function value was
-                 * assigned to, or the first 40 characters of the definition if anonymous.
-                 */
-                String idName = fdn.toString();
-                Source idSource = null;
-                String idOrigin = null;
-                if (ss.getSource() != null) {
-                    idSource = ss.getSource();
-                    String sourceName = idSource.getName();
-                    idOrigin = sourceName;
-                    String packageName = RSource.getPackageName(idSource);
-                    if (packageName != null) {
-                        // try to find the name in the package environments
-                        String functionName = findFunctionName(uid, packageName);
-                        if (functionName != null) {
-                            idName = functionName;
-                        }
-                    } else {
-                        idOrigin = sourceName;
-                    }
-                } else {
-                    // One of the RSyntaxNode "unavailable"s.
-                    idOrigin = idName;
-                    idSource = RSource.fromTextInternal(idName, RSource.Internal.NO_SOURCE);
-                }
-                ident = new FunctionIdentification(idSource, idName, idOrigin, fdn);
-            }
-            return ident;
-
-        }
-    }
-
     /**
      * The function names that were requested to be used in implicit {@code debug(f)} calls, when
      * those functions are defined. Global to all contexts.
      */
     @CompilationFinal private static String[] debugFunctionNames;
 
-    /**
-     * Called back from {@link FunctionDefinitionNode} so that we can record the {@link FunctionUID}
-     * and use {@code fdn} as the canonical {@link FunctionDefinitionNode}.
-     *
-     * @param fdn
-     */
-    public static void registerFunctionDefinition(FunctionDefinitionNode fdn) {
-        // For PerfStats we need to record the info on fdn for the report
-        if (functionMap != null) {
-            FunctionUID uid = fdn.getUID();
-            FunctionData fd = functionMap.get(uid);
-            if (fd != null) {
-                // duplicate
-                return;
-            }
-            assert fd == null;
-            functionMap.put(uid, new FunctionData(uid, fdn));
-        }
-    }
-
-    static FunctionIdentification getFunctionIdentification(FunctionUID uid) {
-        return functionMap.get(uid).getIdentification();
-    }
-
     public static FunctionDefinitionNode getFunctionDefinitionNode(RFunction func) {
         assert !func.isBuiltin();
         return (FunctionDefinitionNode) func.getRootNode();
     }
 
+    public static SourceSection getSourceSection(RFunction func) {
+        return getFunctionDefinitionNode(func).getSourceSection();
+    }
+
     /**
      * Create a filter that matches all the statement nodes in {@code func}.
      */
@@ -183,7 +79,6 @@ public class RInstrumentation {
         builder.sourceIs(fdns.getSource());
         builder.rootSourceSectionEquals(fdns);
         return builder;
-
     }
 
     /**
@@ -201,15 +96,13 @@ public class RInstrumentation {
      * Activate the instrumentation system for {@code context}. Currently this simply checks for the
      * global (command-line) options for tracing and timing. They are applied to every context.
      */
-    public static void activate(@SuppressWarnings("unused") RContext context) {
+    public static void activate(RContext context) {
         String rdebugValue = FastROptions.Rdebug.getStringValue();
         if (rdebugValue != null) {
             debugFunctionNames = rdebugValue.split(",");
         }
-        if (REntryCounters.FunctionListener.enabled() || RNodeTimer.StatementListener.enabled()) {
-            functionMap = new HashMap<>();
-            REntryCounters.FunctionListener.installCounters();
-            RNodeTimer.StatementListener.installTimers();
+        if (RFunctionProfiler.enabled()) {
+            RFunctionProfiler.installTimers(context);
         }
         // Check for function tracing
         RContext.getRRuntimeASTAccess().traceAllFunctions();
@@ -230,66 +123,4 @@ public class RInstrumentation {
         }
     }
 
-    private static Map<FunctionUID, String> functionNameMap;
-
-    /**
-     * Attempts to locate a name for an (assumed) builtin or global function. Returns {@code null}
-     * if not found.
-     */
-    private static String findFunctionName(FunctionUID uid, String packageName) {
-        if (functionNameMap == null) {
-            functionNameMap = new HashMap<>();
-        }
-        String name = functionNameMap.get(uid);
-        if (name == null) {
-            name = findFunctionInPackage(uid, packageName);
-        }
-        return name;
-    }
-
-    /**
-     * Try to find the function identified by uid in the given package. N.B. If we have the uid, the
-     * promise identifying the lazily loaded function must have been evaluated! So there is no need
-     * to evaluate any promises. N.B. For packages, we must use the namespace env as that contains
-     * public and private functions.
-     */
-    private static String findFunctionInPackage(FunctionUID uid, String packageName) {
-        if (packageName == null) {
-            return findFunctionInEnv(uid, REnvironment.globalEnv());
-        }
-        REnvironment env = REnvironment.lookupOnSearchPath(packageName);
-        env = env.getPackageNamespaceEnv();
-        return findFunctionInEnv(uid, env);
-    }
-
-    private static String findFunctionInEnv(FunctionUID uid, REnvironment env) {
-        // This is rather inefficient, but it doesn't matter
-        RStringVector names = env.ls(true, null, false);
-        for (int i = 0; i < names.getLength(); i++) {
-            String name = names.getDataAt(i);
-            Object val = env.get(name);
-            if (val instanceof RPromise) {
-                RPromise prVal = (RPromise) val;
-                if (prVal.isEvaluated()) {
-                    val = prVal.getValue();
-                } else {
-                    continue;
-                }
-            }
-            if (val instanceof RFunction) {
-                RFunction func = (RFunction) val;
-                RootNode rootNode = func.getRootNode();
-                if (rootNode instanceof FunctionDefinitionNode) {
-                    FunctionDefinitionNode fdn = (FunctionDefinitionNode) rootNode;
-                    if (fdn.getUID().equals(uid)) {
-                        functionNameMap.put(fdn.getUID(), name);
-                        return name;
-                    }
-                }
-            }
-        }
-        // Most likely a nested function, which is ok
-        // because they are not lazy and so have names from the parser.
-        return null;
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RNodeTimer.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RNodeTimer.java
deleted file mode 100644
index 84c3cb1927b446d10a3702acd33193a63d837293..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RNodeTimer.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.nodes.instrumentation;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
-
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrumentation.EventContext;
-import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
-import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
-import com.oracle.truffle.api.instrumentation.StandardTags;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.nodes.control.BlockNode;
-import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
-import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation.FunctionIdentification;
-import com.oracle.truffle.r.runtime.FunctionUID;
-import com.oracle.truffle.r.runtime.RPerfStats;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.nodes.RNode;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxNodeVisitor;
-
-/**
- * Basic support for adding as timer to a node. Currently limited to "statement" timing.
- *
- * The instrument records the cumulative time spent executing this node during the process execution
- * using {@link System#nanoTime()}.
- *
- */
-public class RNodeTimer {
-
-    public static final class TimeInfo {
-        private final Object ident;
-        protected long enterTime;
-        protected long cumulativeTime;
-
-        TimeInfo(Object ident) {
-            this.ident = ident;
-        }
-
-        public long getTime() {
-            return cumulativeTime;
-        }
-
-        public Object getIdent() {
-            return ident;
-        }
-    }
-
-    private abstract static class BasicListener implements ExecutionEventListener {
-        private HashMap<SourceSection, TimeInfo> timeInfoMap = new HashMap<>();
-
-        private TimeInfo getTimeInfo(EventContext context) {
-            SourceSection ss = context.getInstrumentedSourceSection();
-            TimeInfo timeInfo = timeInfoMap.get(ss);
-            if (timeInfo == null) {
-                Object obj = timeInfoCreated(context);
-                timeInfo = new TimeInfo(obj);
-                timeInfoMap.put(ss, timeInfo);
-            }
-            return timeInfo;
-        }
-
-        protected TimeInfo getTimeInfo(SourceSection sourceSection) {
-            TimeInfo timeInfo = timeInfoMap.get(sourceSection);
-            assert timeInfo != null;
-            return timeInfo;
-        }
-
-        protected Map<SourceSection, TimeInfo> getTimeInfoMap() {
-            return timeInfoMap;
-        }
-
-        @Override
-        public void onEnter(EventContext context, VirtualFrame frame) {
-            getTimeInfo(context).enterTime = System.nanoTime();
-        }
-
-        @Override
-        public void onReturnValue(EventContext context, VirtualFrame frame, Object result) {
-            TimeInfo timeInfo = getTimeInfo(context);
-            timeInfo.cumulativeTime += System.nanoTime() - timeInfo.enterTime;
-        }
-
-        @Override
-        public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) {
-            onReturnValue(context, frame, exception);
-        }
-
-        protected abstract Object timeInfoCreated(EventContext context);
-
-    }
-
-    public static class StatementListener extends BasicListener {
-        private static final StatementListener singleton = new StatementListener();
-
-        public static long findTimer(RFunction func) {
-            FunctionDefinitionNode fdn = (FunctionDefinitionNode) func.getRootNode();
-            FunctionUID uid = fdn.getUID();
-            long cumTime = 0;
-            for (Map.Entry<SourceSection, TimeInfo> entry : StatementListener.singleton.getTimeInfoMap().entrySet()) {
-                TimeInfo timeInfo = entry.getValue();
-                Node node = (Node) timeInfo.getIdent();
-                FunctionDefinitionNode entryFdn = (FunctionDefinitionNode) node.getRootNode();
-                FunctionUID entryUid = entryFdn.getUID();
-                if (entryUid.equals(uid)) {
-                    // statement in "func"
-                    cumTime += timeInfo.cumulativeTime;
-                }
-            }
-            return cumTime;
-        }
-
-        static void installTimers() {
-            if (enabled()) {
-                SourceSectionFilter.Builder builder = SourceSectionFilter.newBuilder();
-                builder.tagIs(StandardTags.StatementTag.class);
-                SourceSectionFilter filter = builder.build();
-                RInstrumentation.getInstrumenter().attachListener(filter, singleton);
-            }
-        }
-
-        public static void installTimer(RFunction func) {
-            RInstrumentation.getInstrumenter().attachListener(RInstrumentation.createFunctionStatementFilter(func).build(), singleton);
-        }
-
-        @Override
-        protected Node timeInfoCreated(EventContext context) {
-            return context.getInstrumentedNode();
-        }
-
-        // PerfStats support
-
-        static {
-            RPerfStats.register(new PerfHandler());
-        }
-
-        static boolean enabled() {
-            return RPerfStats.enabled(PerfHandler.NAME);
-        }
-
-        private static class TimingData implements Comparable<TimingData> {
-            long time;
-            FunctionUID functionUID;
-
-            TimingData(FunctionUID functionUID) {
-                this.functionUID = functionUID;
-            }
-
-            void addTime(long t) {
-                this.time += t;
-            }
-
-            @Override
-            public int compareTo(TimingData o) {
-                if (time < o.time) {
-                    return 1;
-                } else if (time > o.time) {
-                    return -1;
-                } else {
-                    return 0;
-                }
-            }
-        }
-
-        private static class PerfHandler implements RPerfStats.Handler {
-            static final String NAME = "timing";
-            private boolean stmts;
-            private int threshold;
-
-            @Override
-            public void initialize(String optionText) {
-                if (optionText.length() > 0) {
-                    String[] subOptions = optionText.split(":");
-                    for (String subOption : subOptions) {
-                        if (subOption.equals("stmts")) {
-                            stmts = true;
-                        } else if (subOption.startsWith("threshold")) {
-                            threshold = Integer.parseInt(subOption.substring(subOption.indexOf('=') + 1)) * 1000;
-                        }
-                    }
-                }
-            }
-
-            @Override
-            public String getName() {
-                return NAME;
-            }
-
-            /**
-             * Report the statement timing information at the end of the run. The report is per
-             * function {@link FunctionUID}, which uniquely defines a function in the face of call
-             * target splitting. Functions that consumed less time than requested threshold (default
-             * 0) are not included in the report. The report is sorted by cumulative time.
-             */
-            @Override
-            public void report() {
-                Map<FunctionUID, TimingData> functionMap = new TreeMap<>();
-
-                for (Map.Entry<SourceSection, TimeInfo> entry : StatementListener.singleton.getTimeInfoMap().entrySet()) {
-                    TimeInfo timeInfo = entry.getValue();
-                    Node node = (Node) timeInfo.getIdent();
-                    if (node.getRootNode() instanceof FunctionDefinitionNode) {
-                        FunctionDefinitionNode fdn = (FunctionDefinitionNode) node.getRootNode();
-                        FunctionUID uid = fdn.getUID();
-                        TimingData timingData = functionMap.get(uid);
-                        if (timingData == null) {
-                            timingData = new TimingData(uid);
-                            functionMap.put(uid, timingData);
-                        }
-                        timingData.addTime(millis(entry.getValue().cumulativeTime));
-                    }
-                }
-
-                Collection<TimingData> values = functionMap.values();
-                TimingData[] sortedData = new TimingData[values.size()];
-                values.toArray(sortedData);
-                Arrays.sort(sortedData);
-                long totalTime = 0;
-                for (TimingData t : sortedData) {
-                    totalTime += t.time;
-                }
-
-                RPerfStats.out().printf("Total (user) time %d ms%n", totalTime);
-                for (TimingData t : sortedData) {
-                    if (t.time > 0) {
-                        if (t.time > threshold) {
-                            FunctionIdentification fdi = RInstrumentation.getFunctionIdentification(t.functionUID);
-                            RPerfStats.out().println("==========");
-                            RPerfStats.out().printf("%d ms (%.2f%%): %s, %s%n", t.time, percent(t.time, totalTime), fdi.name, fdi.origin);
-                            if (stmts) {
-                                SourceSection ss = fdi.node.getSourceSection();
-                                if (ss == null) {
-                                    // wrapper
-                                    ss = fdi.node.getBody().getSourceSection();
-                                    if (ss == null) {
-                                        RPerfStats.out().println("no source available");
-                                    }
-                                } else {
-                                    long[] time = createLineTimes(fdi);
-                                    int startLine = ss.getStartLine();
-                                    int lastLine = ss.getEndLine();
-                                    for (int i = startLine; i <= lastLine; i++) {
-                                        RPerfStats.out().printf("%8dms: %s%n", time[i], fdi.source.getCode(i));
-                                    }
-
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        private static double percent(long a, long b) {
-            return ((double) a * 100) / b;
-        }
-
-        private abstract static class StatementVisitor implements RSyntaxNodeVisitor {
-            @SuppressWarnings("unused") protected final FunctionUID uid;
-
-            StatementVisitor(FunctionUID uid) {
-                this.uid = uid;
-            }
-
-            @Override
-            public boolean visit(RSyntaxNode node, int depth) {
-                if (node instanceof BlockNode) {
-                    BlockNode sequenceNode = (BlockNode) node;
-                    RNode[] block = sequenceNode.getSequence();
-                    for (int i = 0; i < block.length; i++) {
-                        RSyntaxNode n = block[i].unwrap().asRSyntaxNode();
-                        if (!callback(n)) {
-                            return false;
-                        }
-                    }
-                }
-                return true;
-            }
-
-            protected abstract boolean callback(RSyntaxNode node);
-
-        }
-
-        private static class LineTimesNodeVisitor extends StatementVisitor {
-            private final long[] times;
-
-            LineTimesNodeVisitor(FunctionUID uid, long[] time) {
-                super(uid);
-                this.times = time;
-            }
-
-            @Override
-            protected boolean callback(RSyntaxNode node) {
-                SourceSection ss = node.getSourceSection();
-                TimeInfo timeInfo = singleton.getTimeInfoMap().get(ss);
-                if (timeInfo != null) {
-                    assert ss.getStartLine() != 0;
-                    long stmtTime = millis(timeInfo.cumulativeTime);
-                    times[0] += stmtTime;
-                    times[ss.getStartLine()] += stmtTime;
-                } else {
-                    /*
-                     * This happens because default arguments are not visited during the AST probe
-                     * walk.
-                     */
-                }
-                return true;
-            }
-        }
-
-        private static long millis(long nanos) {
-            return nanos / 1000000;
-        }
-
-        private static long[] createLineTimes(FunctionIdentification fdi) {
-            /*
-             * Although only those lines occupied by the function will actually have entries in the
-             * array, addressing is easier if we allocate an array that is as long as the entire
-             * source. Since there is never a line 0, we use that to compute the total.
-             */
-            final long[] times = new long[fdi.source.getLineCount() + 1];
-            RSyntaxNode.accept(fdi.node.getBody().asNode(), 0, new LineTimesNodeVisitor(fdi.node.getUID(), times), false);
-            return times;
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FunctionUID.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FunctionUID.java
deleted file mode 100644
index 891850463405f5fc2871f9096fba8e106ca7d068..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FunctionUID.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, 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;
-
-/**
- * An abstract UID for a {@code FunctionDefinitionNode}, used by instrumentation code. There may be
- * many clones of a {@code FunctionDefinitionNode}, but they all share the same UID.
- */
-public interface FunctionUID extends Comparable<FunctionUID> {
-
-}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/WithFunctionUID.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/WithFunctionUID.java
deleted file mode 100644
index b4399e5d19bbc3055b63b0c7afd7e62e2ef296d5..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/WithFunctionUID.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 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;
-
-public interface WithFunctionUID {
-
-    FunctionUID getUID();
-}
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 3ba538ba94eb59c674b36efe0154bf3a92fad094..a616a7eddf1da34c0843fb6318d39cf6d4af7756 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
@@ -41,7 +41,6 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.InvalidAssumptionException;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
-import com.oracle.truffle.tools.Profiler;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.LazyDBCache;
 import com.oracle.truffle.r.runtime.PrimitiveMethodsInfo;
@@ -72,7 +71,7 @@ import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.ffi.RFFIContextStateFactory;
-import com.oracle.truffle.r.runtime.instrument.TraceState;
+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;
@@ -253,30 +252,6 @@ public final class RContext extends ExecutionContext implements TruffleObject {
         }
     }
 
-    /**
-     * Captures all state regarding instrumentation.
-     */
-    public static class InstrumentationState {
-        private final Instrumenter instrumenter;
-        private Profiler profiler;
-
-        InstrumentationState(Instrumenter instrumenter) {
-            this.instrumenter = instrumenter;
-        }
-
-        public void setProfiler(Profiler profiler) {
-            this.profiler = profiler;
-        }
-
-        public Profiler getProfiler() {
-            return profiler;
-        }
-
-        public Instrumenter getInstrumenter() {
-            return instrumenter;
-        }
-    }
-
     private final ContextInfo info;
     private final Engine engine;
 
@@ -365,7 +340,6 @@ public final class RContext extends ExecutionContext implements TruffleObject {
     @CompilationFinal private static RContext singleContext;
 
     private final Env env;
-    private final InstrumentationState instrumentationState;
     private final HashMap<String, TruffleObject> exportedSymbols = new HashMap<>();
     private final boolean initial;
     /**
@@ -389,13 +363,12 @@ public final class RContext extends ExecutionContext implements TruffleObject {
     public final ContextState stateRFFI;
     public final RSerialize.ContextStateImpl stateRSerialize;
     public final LazyDBCache.ContextStateImpl stateLazyDBCache;
-    public final TraceState.ContextStateImpl stateTraceHandling;
+    public final InstrumentationState stateInstrumentation;
     public final ContextStateImpl stateInternalCode;
-    public final RprofState stateRprof;
 
     private ContextState[] contextStates() {
         return new ContextState[]{stateREnvVars, stateRProfile, stateROptions, stateREnvironment, stateRErrorHandling, stateRConnection, stateStdConnections, stateRNG, stateRFFI, stateRSerialize,
-                        stateLazyDBCache, stateTraceHandling, stateRprof};
+                        stateLazyDBCache, stateInstrumentation};
     }
 
     private RContext(Env env, Instrumenter instrumenter, boolean isInitial) {
@@ -422,7 +395,6 @@ public final class RContext extends ExecutionContext implements TruffleObject {
         }
 
         this.env = env;
-        this.instrumentationState = new InstrumentationState(instrumenter);
         if (info.getConsoleHandler() == null) {
             throw Utils.fail("no console handler set");
         }
@@ -460,9 +432,8 @@ public final class RContext extends ExecutionContext implements TruffleObject {
         stateRFFI = RFFIContextStateFactory.newContext(this);
         stateRSerialize = RSerialize.ContextStateImpl.newContext(this);
         stateLazyDBCache = LazyDBCache.ContextStateImpl.newContext(this);
-        stateTraceHandling = TraceState.newContext(this);
+        stateInstrumentation = InstrumentationState.newContext(this, instrumenter);
         stateInternalCode = ContextStateImpl.newContext(this);
-        stateRprof = RprofState.newContext(this);
         engine.activate(stateREnvironment);
 
         if (info.getKind() == ContextKind.SHARE_PARENT_RW) {
@@ -517,7 +488,7 @@ public final class RContext extends ExecutionContext implements TruffleObject {
     }
 
     public InstrumentationState getInstrumentationState() {
-        return instrumentationState;
+        return stateInstrumentation;
     }
 
     public ContextKind getKind() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RprofState.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RprofState.java
deleted file mode 100644
index 9124e8f4d054e8bea9ed52ee91c5cd9e2981eb1c..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RprofState.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.context;
-
-import java.io.PrintWriter;
-
-import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
-
-public class RprofState implements RContext.ContextState {
-    private PrintWriter out;
-    private Thread profileThread;
-    private ExecutionEventListener statementListener;
-    private long intervalInMillis;
-    private boolean lineProfiling;
-
-    public static RprofState newContext(@SuppressWarnings("unused") RContext context) {
-        return new RprofState();
-    }
-
-    public void initialize(PrintWriter outA, Thread profileThreadA, ExecutionEventListener statementListenerA, long intervalInMillisA,
-                    boolean lineProfilingA) {
-        this.out = outA;
-        this.profileThread = profileThreadA;
-        this.statementListener = statementListenerA;
-        this.intervalInMillis = intervalInMillisA;
-        this.lineProfiling = lineProfilingA;
-    }
-
-    public boolean lineProfiling() {
-        return lineProfiling;
-    }
-
-    public PrintWriter out() {
-        return out;
-    }
-
-    public long intervalInMillis() {
-        return intervalInMillis;
-    }
-
-    public ExecutionEventListener statementListener() {
-        return statementListener;
-    }
-
-    public Thread profileThread() {
-        return profileThread;
-    }
-
-}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/FunctionUIDFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/FunctionUIDFactory.java
deleted file mode 100644
index c0c02637a774dc0db47a9fe08c0b00bcdd7ecdef..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/FunctionUIDFactory.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.runtime.instrument;
-
-import com.oracle.truffle.r.runtime.FunctionUID;
-import com.oracle.truffle.r.runtime.Utils;
-
-public abstract class FunctionUIDFactory {
-    private static final String FACTORY_CLASS_PROPERTY = "fastr.fuid.factory.class";
-    private static final String PACKAGE_PREFIX = "com.oracle.truffle.r.nodes.function.";
-    private static final String SUFFIX = "FunctionUIDFactory";
-    private static final String DEFAULT_FACTORY = "along";
-    private static final String DEFAULT_FACTORY_CLASS = mapSimpleName(DEFAULT_FACTORY);
-
-    private static String mapSimpleName(String simpleName) {
-        return PACKAGE_PREFIX + simpleName.toUpperCase() + SUFFIX;
-    }
-
-    private static FunctionUIDFactory instance;
-
-    static {
-        String prop = System.getProperty(FACTORY_CLASS_PROPERTY);
-        if (prop != null) {
-            if (!prop.contains(".")) {
-                // simple name
-                prop = mapSimpleName(prop);
-            }
-        } else {
-            prop = DEFAULT_FACTORY_CLASS;
-        }
-        try {
-            instance = (FunctionUIDFactory) Class.forName(prop).newInstance();
-        } catch (Exception ex) {
-            Utils.fail("Failed to instantiate class: " + prop + ": " + ex);
-        }
-    }
-
-    public static FunctionUIDFactory get() {
-        return instance;
-    }
-
-    public abstract FunctionUID createUID();
-}
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
new file mode 100644
index 0000000000000000000000000000000000000000..8ffa8a307cc34835a1cbf2c5719e38131bbbc0e3
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2013, 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.instrument;
+
+import java.io.PrintWriter;
+import java.util.WeakHashMap;
+
+import com.oracle.truffle.api.instrumentation.EventBinding;
+import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
+import com.oracle.truffle.api.instrumentation.Instrumenter;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.tools.Profiler;
+
+/**
+ * The tracingState is a global variable in R, so we store it (and the associated listener objects)
+ * in the {@link RContext}. We also store related {@code debug} state, as that is also context
+ * specific.
+ *
+ */
+public class InstrumentationState implements RContext.ContextState {
+
+    /**
+     * Records all functions that have trace listeners installed.
+     */
+    private final WeakHashMap<SourceSection, EventBinding<?>> traceBindingMap = new WeakHashMap<>();
+
+    private boolean tracingState = true;
+
+    /**
+     * Records all functions that have debug listeners installed.
+     */
+    private final WeakHashMap<SourceSection, ExecutionEventListener> debugListenerMap = new WeakHashMap<>();
+
+    private final Instrumenter instrumenter;
+
+    private Profiler profiler;
+
+    private final RprofState rprofState;
+
+    /**
+     * State used by {@code Rprof}.
+     *
+     */
+    public static class RprofState {
+        private PrintWriter out;
+        private Thread profileThread;
+        private ExecutionEventListener statementListener;
+        private long intervalInMillis;
+        private boolean lineProfiling;
+
+        public static RprofState newContext(@SuppressWarnings("unused") RContext context) {
+            return new RprofState();
+        }
+
+        public void initialize(PrintWriter outA, Thread profileThreadA, ExecutionEventListener statementListenerA, long intervalInMillisA,
+                        boolean lineProfilingA) {
+            this.out = outA;
+            this.profileThread = profileThreadA;
+            this.statementListener = statementListenerA;
+            this.intervalInMillis = intervalInMillisA;
+            this.lineProfiling = lineProfilingA;
+        }
+
+        public boolean lineProfiling() {
+            return lineProfiling;
+        }
+
+        public PrintWriter out() {
+            return out;
+        }
+
+        public long intervalInMillis() {
+            return intervalInMillis;
+        }
+
+        public ExecutionEventListener statementListener() {
+            return statementListener;
+        }
+
+        public Thread profileThread() {
+            return profileThread;
+        }
+
+    }
+
+    private InstrumentationState(Instrumenter instrumenter) {
+        this.instrumenter = instrumenter;
+        this.rprofState = new RprofState();
+    }
+
+    public void putTraceBinding(SourceSection ss, EventBinding<?> binding) {
+        traceBindingMap.put(ss, binding);
+    }
+
+    public EventBinding<?> getTraceBinding(SourceSection ss) {
+        return traceBindingMap.get(ss);
+    }
+
+    public void putDebugListener(SourceSection ss, ExecutionEventListener listener) {
+        debugListenerMap.put(ss, listener);
+    }
+
+    public EventBinding<?>[] getTraceBindings() {
+        EventBinding<?>[] result = new EventBinding<?>[traceBindingMap.size()];
+        traceBindingMap.values().toArray(result);
+        return result;
+
+    }
+
+    public ExecutionEventListener getDebugListener(SourceSection ss) {
+        return debugListenerMap.get(ss);
+    }
+
+    public boolean setTracingState(boolean state) {
+        boolean prev = tracingState;
+        tracingState = state;
+        return prev;
+    }
+
+    public boolean getTracingState() {
+        return tracingState;
+    }
+
+    public void setProfiler(Profiler profiler) {
+        this.profiler = profiler;
+    }
+
+    public Profiler getProfiler() {
+        return profiler;
+    }
+
+    public Instrumenter getInstrumenter() {
+        return instrumenter;
+    }
+
+    public RprofState getRprof() {
+        return rprofState;
+    }
+
+    public static InstrumentationState newContext(@SuppressWarnings("unused") RContext context, Instrumenter instrumenter) {
+        return new InstrumentationState(instrumenter);
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/TraceState.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/TraceState.java
deleted file mode 100644
index 4bbf3d547340fd38c04948b4f776cc6fdd9a626e..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/TraceState.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2013, 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.instrument;
-
-import java.util.WeakHashMap;
-
-import com.oracle.truffle.r.runtime.FunctionUID;
-import com.oracle.truffle.r.runtime.context.RContext;
-
-/**
- * The tracingState is a global variable in R, so we store it (and the associated listener objects)
- * in the {@link RContext}. To finesse the temporary existence of two instrumentation frameworks the
- * receiver is typed as {@link Object}.
- *
- */
-public class TraceState {
-    public static class ContextStateImpl implements RContext.ContextState {
-
-        /**
-         * Records all functions that have trace listeners installed.
-         */
-        private final WeakHashMap<FunctionUID, Object> listenerMap = new WeakHashMap<>();
-        private boolean tracingState = true;
-
-        public void put(FunctionUID functionUID, Object listener) {
-            listenerMap.put(functionUID, listener);
-        }
-
-        public Object get(FunctionUID functionUID) {
-            return listenerMap.get(functionUID);
-        }
-
-        public boolean setTracingState(boolean state) {
-            boolean prev = tracingState;
-            tracingState = state;
-            return prev;
-        }
-
-        public boolean getTracingState() {
-            return tracingState;
-        }
-
-        public Object[] getListeners() {
-            Object[] result = new Object[listenerMap.size()];
-            listenerMap.values().toArray(result);
-            return result;
-        }
-    }
-
-    public static ContextStateImpl newContext(@SuppressWarnings("unused") RContext context) {
-        return new ContextStateImpl();
-    }
-}