diff graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProbeNode.java @ 13455:69d2e4baa215

Truffle: new infrastructure related to instrumentation, and in particular debugging: support for managing Source objects; framework for generalized "instrumentation proxy nodes" (to be inserted into ASTs with no runtime cost when inactive), and "probes" (which can be attached to proxy nodes to receive event notification); a rudimentary interface and abstract implementation for a "debug manager" (mostly a placeholder at this point); and the beginning of a language-agnostic ExecutionContext interface.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 17 Dec 2013 20:22:45 -0800
parents
children b88852aea6f3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProbeNode.java	Tue Dec 17 20:22:45 2013 -0800
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2013, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.api.nodes.instrument;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * A <strong>probe</strong>: a Truffle instrumentation node that holds code to perform some action
+ * when notified (via a {@linkplain InstrumentationProxyNode proxy node} in the AST) of a
+ * {@linkplain InstrumentationProbeEvents probe event} taking place at the AST node.
+ * <p>
+ * Probes are only active when attached to a {@linkplain ProbeChain "probe chain"} that is referred
+ * to by one or more {@linkplain InstrumentationProxyNode proxy nodes} in an AST.
+ */
+public abstract class InstrumentationProbeNode extends Node implements InstrumentationNode, InstrumentationProbeEvents {
+
+    /**
+     * Next in chain.
+     */
+    @Child protected InstrumentationProbeNode next;
+
+    protected InstrumentationProbeNode() {
+    }
+
+    protected int countProbes() {
+        return next == null ? 0 : next.countProbes() + 1;
+    }
+
+    /**
+     * Add a probe to the end of this probe chain.
+     */
+    protected void internalAppendProbe(InstrumentationProbeNode newProbeNode) {
+        if (next == null) {
+            this.next = adoptChild(newProbeNode);
+        } else {
+            next.internalAppendProbe(newProbeNode);
+        }
+    }
+
+    protected void internalRemoveProbe(InstrumentationProbeNode oldProbeNode) {
+        if (next == null) {
+            throw new RuntimeException("Couldn't find probe to remove: " + oldProbeNode);
+        } else if (next == oldProbeNode) {
+            if (oldProbeNode.next == null) {
+                this.next = null;
+            } else {
+                this.next = adoptChild(oldProbeNode.next);
+            }
+        } else {
+            next.internalRemoveProbe(oldProbeNode);
+        }
+    }
+
+    /**
+     * Passes up the chain notification that a probe has changed its execution state in a way that
+     * invalidates fast path code. Assumes that there is an instance of {@link ProbeChain} at the
+     * head of the chain.
+     */
+    @CompilerDirectives.SlowPath
+    protected void notifyProbeChanged(InstrumentationProbeNode probeNode) {
+        final InstrumentationProbeNode parent = (InstrumentationProbeNode) getParent();
+        parent.notifyProbeChanged(probeNode);
+    }
+
+    // TODO (mlvdv) making the internal*() methods public is a workaround for a bug/limitation in
+    // the Truffle compiler; they are intended to be private.
+
+    public void internalEnter(Node astNode, VirtualFrame frame) {
+        enter(astNode, frame);
+        if (next != null) {
+            next.internalEnter(astNode, frame);
+        }
+    }
+
+    public void internalLeave(Node astNode, VirtualFrame frame) {
+        leave(astNode, frame);
+        if (next != null) {
+            next.internalLeave(astNode, frame);
+        }
+    }
+
+    public void internalLeave(Node astNode, VirtualFrame frame, boolean result) {
+        leave(astNode, frame, result);
+        if (next != null) {
+            next.internalLeave(astNode, frame, result);
+        }
+    }
+
+    public void internalLeave(Node astNode, VirtualFrame frame, byte result) {
+        leave(astNode, frame, result);
+        if (next != null) {
+            next.internalLeave(astNode, frame, result);
+        }
+    }
+
+    public void internalLeave(Node astNode, VirtualFrame frame, short result) {
+        leave(astNode, frame, result);
+        if (next != null) {
+            next.internalLeave(astNode, frame, result);
+        }
+    }
+
+    public void internalLeave(Node astNode, VirtualFrame frame, int result) {
+        leave(astNode, frame, result);
+        if (next != null) {
+            next.internalLeave(astNode, frame, result);
+        }
+    }
+
+    public void internalLeave(Node astNode, VirtualFrame frame, long result) {
+        leave(astNode, frame, result);
+        if (next != null) {
+            next.internalLeave(astNode, frame, result);
+        }
+    }
+
+    public void internalLeave(Node astNode, VirtualFrame frame, char result) {
+        leave(astNode, frame, result);
+        if (next != null) {
+            next.internalLeave(astNode, frame, result);
+        }
+    }
+
+    public void internalLeave(Node astNode, VirtualFrame frame, float result) {
+        leave(astNode, frame, result);
+        if (next != null) {
+            next.internalLeave(astNode, frame, result);
+        }
+    }
+
+    public void internalLeave(Node astNode, VirtualFrame frame, double result) {
+        leave(astNode, frame, result);
+        if (next != null) {
+            next.internalLeave(astNode, frame, result);
+        }
+    }
+
+    public void internalLeave(Node astNode, VirtualFrame frame, Object result) {
+        leave(astNode, frame, result);
+        if (next != null) {
+            next.internalLeave(astNode, frame, result);
+        }
+    }
+
+    public void internalLeaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
+        leaveExceptional(astNode, frame, null);
+        if (next != null) {
+            next.internalLeaveExceptional(astNode, frame, e);
+        }
+    }
+
+    public void internalReplace(Node oldAstNode, Node newAstNode, String reason) {
+        replace(oldAstNode, newAstNode, reason);
+        if (next != null) {
+            next.internalReplace(oldAstNode, newAstNode, reason);
+        }
+    }
+
+    /**
+     * A probe implementation that implements all of {@link InstrumentationProbeEvents} with empty
+     * methods; concrete subclasses can override only the methods for which something is to be done.
+     */
+    public static class DefaultProbeNode extends InstrumentationProbeNode {
+
+        public void enter(Node astNode, VirtualFrame frame) {
+        }
+
+        public void leave(Node astNode, VirtualFrame frame) {
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, boolean result) {
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, byte result) {
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, short result) {
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, int result) {
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, long result) {
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, char result) {
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, float result) {
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, double result) {
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, Object result) {
+        }
+
+        public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
+        }
+
+        public void replace(Node oldAstNode, Node newAstNode, String reason) {
+        }
+
+    }
+
+    /**
+     * Holder of a chain of {@linkplain InstrumentationProbeNode probes}: manages the
+     * {@link Assumption} that the chain has not changed since checked checked.
+     */
+    public static final class ProbeChain extends DefaultProbeNode {
+
+        @CompilerDirectives.CompilationFinal private Assumption probeUnchanged;
+
+        /**
+         * Source information about the node to which this probe chain is attached; it isn't
+         * otherwise available. A probe chain is shared by every copy made during runtime, so there
+         * is no parent pointer.
+         */
+        @SuppressWarnings("unused") private final SourceSection sourceSection;
+
+        private final String description; // for debugging
+
+        /**
+         * Creates a new, empty chain of {@linkplain InstrumentationProbeNode probes}, to which
+         * probes can be added/removed, and all of which will be notified of
+         * {@linkplain InstrumentationProbeEvents events} when the chain is notified.
+         */
+        public ProbeChain(SourceSection sourceSection, String description) {
+            this.probeUnchanged = Truffle.getRuntime().createAssumption();
+            this.sourceSection = sourceSection;
+            this.description = description;
+            this.next = null;
+        }
+
+        public int probeCount() {
+            return countProbes();
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        @Override
+        protected int countProbes() {
+            // The head of the chain does not itself hold a probe
+            return next == null ? 0 : next.countProbes();
+        }
+
+        @Override
+        protected void notifyProbeChanged(InstrumentationProbeNode probeNode) {
+            probeUnchanged.invalidate();
+            probeUnchanged = Truffle.getRuntime().createAssumption();
+        }
+
+        @CompilerDirectives.SlowPath
+        public void appendProbe(InstrumentationProbeNode newProbeNode) {
+            probeUnchanged.invalidate();
+            super.internalAppendProbe(newProbeNode);
+            probeUnchanged = Truffle.getRuntime().createAssumption();
+        }
+
+        @CompilerDirectives.SlowPath
+        public void removeProbe(InstrumentationProbeNode oldProbeNode) {
+            probeUnchanged.invalidate();
+            super.internalRemoveProbe(oldProbeNode);
+            probeUnchanged = Truffle.getRuntime().createAssumption();
+        }
+
+        public void notifyEnter(Node astNode, VirtualFrame frame) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalEnter(astNode, frame);
+            }
+        }
+
+        public void notifyLeave(Node astNode, VirtualFrame frame) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame);
+            }
+        }
+
+        public void notifyLeave(Node astNode, VirtualFrame frame, boolean result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void notifyLeave(Node astNode, VirtualFrame frame, byte result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void notifyLeave(Node astNode, VirtualFrame frame, short result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void notifyLeave(Node astNode, VirtualFrame frame, int result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void notifyLeave(Node astNode, VirtualFrame frame, long result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void notifyLeave(Node astNode, VirtualFrame frame, char result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void notifyLeave(Node astNode, VirtualFrame frame, float result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void notifyLeave(Node astNode, VirtualFrame frame, double result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void notifyLeave(Node astNode, VirtualFrame frame, Object result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void notifyLeaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeaveExceptional(astNode, frame, e);
+            }
+        }
+
+        public void notifyReplace(Node oldAstNode, Node newAstNode, String reason) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalReplace(oldAstNode, newAstNode, reason);
+            }
+        }
+
+    }
+
+}