diff truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java @ 21951:9c8c0937da41

Moving all sources into truffle subdirectory
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Wed, 17 Jun 2015 10:58:08 +0200
parents graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java@36285949c1d5
children ff6f34159b8a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Wed Jun 17 10:58:08 2015 +0200
@@ -0,0 +1,636 @@
+/*
+ * Copyright (c) 2013, 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.  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.instrument;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.*;
+
+// TODO (mlvdv) these statics should not be global.  Move them to some kind of context.
+// TODO (mlvdv) migrate factory (together with Probe)? break out nested classes?
+
+/**
+ * A <em>binding</em> between:
+ * <ol>
+ * <li>A {@link Probe}: a source of <em>execution events</em> taking place at a program location in
+ * an executing Truffle AST, and</li>
+ * <li>A <em>listener</em>: a consumer of execution events on behalf of an external client.
+ * </ol>
+ * <p>
+ * Client-oriented documentation for the use of Instruments is available online at <a
+ * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events"
+ * >https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a>
+ * <p>
+ * The implementation of Instruments is complicated by the requirement that Truffle be able to clone
+ * ASTs at any time. In particular, any instrumentation-supporting Nodes that have been attached to
+ * an AST must be cloned along with the AST: AST clones are not permitted to share Nodes.
+ * <p>
+ * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is encouraged
+ * by providing the {@link SimpleInstrumentListener} for clients that need know nothing more than
+ * the properties associated with a Probe: it's {@link SourceSection} and any associated instances
+ * of {@link SyntaxTag}.
+ * <p>
+ * AST cloning is <em>not transparent</em> to clients that use the
+ * {@link StandardInstrumentListener}, since those event methods identify the concrete Node instance
+ * (and thus the AST instance) where the event takes place.
+ * <p>
+ * <h4>Implementation Notes: the Life Cycle of an {@link Instrument} at a {@link Probe}</h4>
+ * <p>
+ * <ul>
+ * <li>A new Instrument is created in permanent association with a client-provided
+ * <em>listener.</em></li>
+ *
+ * <li>Multiple Instruments may share a single listener.</li>
+ *
+ * <li>An Instrument does nothing until it is {@linkplain Probe#attach(Instrument) attached} to a
+ * Probe, at which time the Instrument begins routing execution events from the Probe's AST location
+ * to the Instrument's listener.</li>
+ *
+ * <li>Neither Instruments nor Probes are {@link Node}s.</li>
+ *
+ * <li>A Probe has a single source-based location in an AST, but manages a separate
+ * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li>
+ * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is cloned
+ * along with the rest of the AST.</li>
+ *
+ * <li>When a new Instrument (for example an instance of {@link SimpleInstrument} is attached to a
+ * Probe, the Instrument inserts a new instance of its private Node type,
+ * {@link SimpleInstrument.SimpleInstrumentNode}, into <em>each of the instrument chains</em>
+ * managed by the Probe, i.e. one node instance per existing clone of the AST.</li>
+ *
+ * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the
+ * Instrument's private Node type will be cloned along with the rest of the the AST.</li>
+ * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the
+ * shared (outer) Instrument instance; that state includes a reference to the Instrument's listener.
+ * </li>
+ *
+ * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed}, the
+ * Instrument searches every instrument chain associated with the Probe and removes the instance of
+ * its private Node type.</li>
+ *
+ * <li>Attaching and disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of the
+ * AST.</li>
+ *
+ * </ul>
+ *
+ * @see Probe
+ * @see TruffleEvents
+ */
+public abstract class Instrument {
+
+    /**
+     * Creates a <em>Simple Instrument</em>: this Instrument routes execution events to a
+     * client-provided listener.
+     *
+     * @param listener a listener for execution events
+     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
+     * @return a new instrument, ready for attachment at a probe
+     */
+    public static Instrument create(SimpleInstrumentListener listener, String instrumentInfo) {
+        return new SimpleInstrument(listener, instrumentInfo);
+    }
+
+    /**
+     * Creates a <em>Standard Instrument</em>: this Instrument routes execution events, together
+     * with access to Truffle execution state, to a client-provided listener.
+     *
+     * @param standardListener a listener for execution events and execution state
+     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
+     * @return a new instrument, ready for attachment at a probe
+     */
+    public static Instrument create(StandardInstrumentListener standardListener, String instrumentInfo) {
+        return new StandardInstrument(standardListener, instrumentInfo);
+    }
+
+    /**
+     * Creates an <em>Advanced Instrument</em>: this Instrument executes efficiently, subject to
+     * full Truffle optimization, a client-provided AST fragment every time the Probed node is
+     * entered.
+     * <p>
+     * Any {@link RuntimeException} thrown by execution of the fragment is caught by the framework
+     * and reported to the listener; there is no other notification.
+     *
+     * @param resultListener optional client callback for results/failure notification
+     * @param rootFactory provider of AST fragments on behalf of the client
+     * @param requiredResultType optional requirement, any non-assignable result is reported to the
+     *            the listener, if any, as a failure
+     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
+     * @return a new instrument, ready for attachment at a probe
+     */
+    public static Instrument create(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
+        return new AdvancedInstrument(resultListener, rootFactory, requiredResultType, instrumentInfo);
+    }
+
+    // TODO (mlvdv) experimental
+    /**
+     * For implementation testing.
+     */
+    public static Instrument create(TruffleOptListener listener) {
+        return new TruffleOptInstrument(listener, null);
+    }
+
+    /**
+     * Has this instrument been disposed? stays true once set.
+     */
+    private boolean isDisposed = false;
+
+    protected Probe probe = null;
+
+    /**
+     * Optional documentation, mainly for debugging.
+     */
+    private final String instrumentInfo;
+
+    private Instrument(String instrumentInfo) {
+        this.instrumentInfo = instrumentInfo;
+    }
+
+    /**
+     * Gets the {@link Probe} to which this Instrument is currently attached: {@code null} if not
+     * yet attached to a Probe or if this Instrument has been {@linkplain #dispose() disposed}.
+     */
+    public Probe getProbe() {
+        return probe;
+    }
+
+    /**
+     * Removes this Instrument from the Probe to which it attached and renders this Instrument
+     * inert.
+     *
+     * @throws IllegalStateException if this instrument has already been disposed
+     */
+    public void dispose() throws IllegalStateException {
+        if (isDisposed) {
+            throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt");
+        }
+        if (probe != null) {
+            // It's attached
+            probe.disposeInstrument(this);
+            probe = null;
+        }
+        this.isDisposed = true;
+    }
+
+    /**
+     * For internal implementation only.
+     */
+    void setAttachedTo(Probe probe) {
+        this.probe = probe;
+    }
+
+    /**
+     * Has this instrument been disposed and rendered unusable?
+     */
+    boolean isDisposed() {
+        return isDisposed;
+    }
+
+    abstract AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode);
+
+    /**
+     * Removes this instrument from an instrument chain.
+     */
+    abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode);
+
+    /**
+     * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}.
+     */
+    private static final class SimpleInstrument extends Instrument {
+
+        /**
+         * Tool-supplied listener for events.
+         */
+        private final SimpleInstrumentListener simpleListener;
+
+        private SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) {
+            super(instrumentInfo);
+            this.simpleListener = simpleListener;
+        }
+
+        @Override
+        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
+            return new SimpleInstrumentNode(nextNode);
+        }
+
+        @Override
+        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrumentNode;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(SimpleInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        /**
+         * Node that implements a {@link SimpleInstrument} in a particular AST.
+         */
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class SimpleInstrumentNode extends AbstractInstrumentNode {
+
+            private SimpleInstrumentNode(AbstractInstrumentNode nextNode) {
+                super(nextNode);
+            }
+
+            public void enter(Node node, VirtualFrame vFrame) {
+                SimpleInstrument.this.simpleListener.enter(SimpleInstrument.this.probe);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
+                }
+            }
+
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                SimpleInstrument.this.simpleListener.returnVoid(SimpleInstrument.this.probe);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
+                }
+            }
+
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                SimpleInstrument.this.simpleListener.returnValue(SimpleInstrument.this.probe, result);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
+                }
+            }
+
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                SimpleInstrument.this.simpleListener.returnExceptional(SimpleInstrument.this.probe, exception);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : simpleListener.getClass().getSimpleName();
+            }
+        }
+    }
+
+    /**
+     * An instrument that propagates events to an instance of {@link StandardInstrumentListener}.
+     */
+    private static final class StandardInstrument extends Instrument {
+
+        /**
+         * Tool-supplied listener for AST events.
+         */
+        private final StandardInstrumentListener standardListener;
+
+        private StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) {
+            super(instrumentInfo);
+            this.standardListener = standardListener;
+        }
+
+        @Override
+        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
+            return new StandardInstrumentNode(nextNode);
+        }
+
+        @Override
+        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrumentNode;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(StandardInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        /**
+         * Node that implements a {@link StandardInstrument} in a particular AST.
+         */
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class StandardInstrumentNode extends AbstractInstrumentNode {
+
+            private StandardInstrumentNode(AbstractInstrumentNode nextNode) {
+                super(nextNode);
+            }
+
+            public void enter(Node node, VirtualFrame vFrame) {
+                standardListener.enter(StandardInstrument.this.probe, node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
+                }
+            }
+
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                standardListener.returnVoid(StandardInstrument.this.probe, node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
+                }
+            }
+
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                standardListener.returnValue(StandardInstrument.this.probe, node, vFrame, result);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
+                }
+            }
+
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                standardListener.returnExceptional(StandardInstrument.this.probe, node, vFrame, exception);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : standardListener.getClass().getSimpleName();
+            }
+        }
+    }
+
+    /**
+     * An instrument that allows clients to provide an AST fragment to be executed directly from
+     * within a Probe's <em>instrumentation chain</em>, and thus directly in the executing Truffle
+     * AST with potential for full optimization.
+     */
+    private static final class AdvancedInstrument extends Instrument {
+
+        private final AdvancedInstrumentResultListener resultListener;
+        private final AdvancedInstrumentRootFactory rootFactory;
+        private final Class<?> requiredResultType;
+
+        private AdvancedInstrument(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
+            super(instrumentInfo);
+            this.resultListener = resultListener;
+            this.rootFactory = rootFactory;
+            this.requiredResultType = requiredResultType;
+        }
+
+        @Override
+        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
+            return new AdvancedInstrumentNode(nextNode);
+        }
+
+        @Override
+        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrumentNode;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(AdvancedInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        /**
+         * Node that implements a {@link AdvancedInstrument} in a particular AST.
+         */
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class AdvancedInstrumentNode extends AbstractInstrumentNode {
+
+            @Child private AdvancedInstrumentRoot instrumentRoot;
+
+            private AdvancedInstrumentNode(AbstractInstrumentNode nextNode) {
+                super(nextNode);
+            }
+
+            public void enter(Node node, VirtualFrame vFrame) {
+                if (instrumentRoot == null) {
+                    try {
+                        final AdvancedInstrumentRoot newInstrumentRoot = AdvancedInstrument.this.rootFactory.createInstrumentRoot(AdvancedInstrument.this.probe, node);
+                        if (newInstrumentRoot != null) {
+                            instrumentRoot = newInstrumentRoot;
+                            adoptChildren();
+                            AdvancedInstrument.this.probe.invalidateProbeUnchanged();
+                        }
+                    } catch (RuntimeException ex) {
+                        if (resultListener != null) {
+                            resultListener.notifyFailure(node, vFrame, ex);
+                        }
+                    }
+                }
+                if (instrumentRoot != null) {
+                    try {
+                        final Object result = instrumentRoot.executeRoot(node, vFrame);
+                        if (resultListener != null) {
+                            checkResultType(result);
+                            resultListener.notifyResult(node, vFrame, result);
+                        }
+                    } catch (RuntimeException ex) {
+                        if (resultListener != null) {
+                            resultListener.notifyFailure(node, vFrame, ex);
+                        }
+                    }
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
+                }
+            }
+
+            private void checkResultType(Object result) {
+                if (requiredResultType == null) {
+                    return;
+                }
+                if (result == null) {
+                    throw new RuntimeException("Instrument result null: " + requiredResultType.getSimpleName() + " is required");
+                }
+                if (!(requiredResultType.isAssignableFrom(result.getClass()))) {
+                    throw new RuntimeException("Instrument result " + result.toString() + " not assignable to " + requiredResultType.getSimpleName());
+                }
+            }
+
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
+                }
+            }
+
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
+                }
+            }
+
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : rootFactory.getClass().getSimpleName();
+            }
+        }
+    }
+
+    public interface TruffleOptListener {
+        void notifyIsCompiled(boolean isCompiled);
+    }
+
+    private static final class TruffleOptInstrument extends Instrument {
+
+        private final TruffleOptListener toolOptListener;
+
+        private TruffleOptInstrument(TruffleOptListener listener, String instrumentInfo) {
+            super(instrumentInfo);
+            this.toolOptListener = listener;
+        }
+
+        @Override
+        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
+            return new TruffleOptInstrumentNode(nextNode);
+        }
+
+        @Override
+        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrumentNode;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(TruffleOptInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class TruffleOptInstrumentNode extends AbstractInstrumentNode {
+
+            private boolean isCompiled;
+
+            private TruffleOptInstrumentNode(AbstractInstrumentNode nextNode) {
+                super(nextNode);
+                this.isCompiled = CompilerDirectives.inCompiledCode();
+            }
+
+            public void enter(Node node, VirtualFrame vFrame) {
+                if (this.isCompiled != CompilerDirectives.inCompiledCode()) {
+                    this.isCompiled = CompilerDirectives.inCompiledCode();
+                    TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled);
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
+                }
+            }
+
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
+                }
+            }
+
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
+                }
+            }
+
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : toolOptListener.getClass().getSimpleName();
+            }
+        }
+    }
+
+    @NodeInfo(cost = NodeCost.NONE)
+    abstract class AbstractInstrumentNode extends Node implements TruffleEvents, InstrumentationNode {
+
+        @Child protected AbstractInstrumentNode nextInstrumentNode;
+
+        protected AbstractInstrumentNode(AbstractInstrumentNode nextNode) {
+            this.nextInstrumentNode = nextNode;
+        }
+
+        @Override
+        public boolean isInstrumentable() {
+            return false;
+        }
+
+        /**
+         * Gets the instrument that created this node.
+         */
+        private Instrument getInstrument() {
+            return Instrument.this;
+        }
+
+        /**
+         * Removes the node from this chain that was added by a particular instrument, assuming that
+         * the head of the chain is not the one to be replaced. This is awkward, but is required
+         * because {@link Node#replace(Node)} won't take a {@code null} argument. This doesn't work
+         * for the tail of the list, which would be replacing itself with null. So the replacement
+         * must be directed the parent of the node being removed.
+         */
+        private boolean removeFromChain(Instrument instrument) {
+            assert getInstrument() != instrument;
+            if (nextInstrumentNode == null) {
+                return false;
+            }
+            if (nextInstrumentNode.getInstrument() == instrument) {
+                // Next is the one to remove
+                if (nextInstrumentNode.nextInstrumentNode == null) {
+                    // Next is at the tail; just forget
+                    nextInstrumentNode = null;
+                } else {
+                    // Replace next with its successor
+                    nextInstrumentNode.replace(nextInstrumentNode.nextInstrumentNode);
+                }
+                return true;
+            }
+            return nextInstrumentNode.removeFromChain(instrument);
+        }
+
+        protected String getInstrumentInfo() {
+            return Instrument.this.instrumentInfo;
+        }
+    }
+}