Class ExecutionListener

java.lang.Object
org.graalvm.polyglot.management.ExecutionListener
All Implemented Interfaces:
AutoCloseable

public final class ExecutionListener extends Object implements AutoCloseable
Execution listeners allow to instrument the execution of guest languages. For example, it is possible to attach an execution listeners that is invoked for every statement of the guest language program, similar to how a debugger would single-step through the program.

The following example prints the characters of every executed statement of simple JavaScript loop.

 Context context = Context.create("js");
 ExecutionListeners listener = ExecutionListeners.newBuilder()
          .onEnter((e) -> System.out.println(
                  e.getLocation().getCharacters()))
          .statements(true)
          .attach(context.getEngine());
 context.eval("js", "for (var i = 0; i < 2; i++);");
 listener.close();
 
Prints the following result:
 i = 0
 i < 2
 i++
 i < 2
 i++
 i < 2
 

Creation and Closing

An execution listener builder can be created by first invoking newBuilder(). At least one event consumer and one filtered source element needs to be enabled. To complete the listener attachment ExecutionListener.Builder.attach(Engine) needs to be invoked. Attach may be invoked multiple times for one builder.

All execution listeners are automatically closed when the engine closed. To close a listener earlier close() may be invoked. Execution listeners are AutoCloseable and can therefore be used in try-with-resources blocks.

Event Consumers

The following event consumers can be set for an execution listener:
  • OnEnter: An event that is notified when an execution of an element is entered. This event is consumed before any input values are executed.
  • OnReturn: An event that is notified when an execution of an element was entered and completed.
At least one event consumer needs to be set otherwise an IllegalArgumentException will be thrown by the builder when it is attached.

Event consumers may throw any Java host exception. Such exceptions will be reported to the context as PolyglotException instances. The thrown exception may be accessed using PolyglotException.asHostException().

Event Data

For every event that is consumed the source location and root name data is available. Other event data will return null by default.

The collection of the following event data may be enabled:

If additional event data is collected then the peak performance overhead of execution listeners is significant. It is not recommended to collect additional event data when running production workloads.

Provided event instances may escape the event consumer and remain usable until the engine is closed.

Event Filters

Execution listeners can be applied to the following source elements:
  • Roots: Filter for marked program locations that represent a root of a function, method or closure.
  • Statements: Filter for marked program locations that represent a statement.
  • Expressions: Filter for marked program locations that represent an expression.
At least one source element needs to be enabled otherwise an IllegalArgumentException will be thrown by the builder when it is attached. Not all source elements may be supported by a language. If the language does not support listening to a source element then no events will be triggered.

If multiple source elements are enabled, multiple or one event may be reported per source location. If this behavior is not desirable than multiple execution listeners for each source element can be created and attached.

By default the execution listener is applied to all sources that were loaded. A source filter may be attached to limit the number of sources that will trigger events.

Performance

The peak performance overhead of execution listeners depend on the granularity of the filter. Roots can be collected more efficiently than statement or expression events due to their frequency. If additional event data is collected then the peak performance overhead of execution listeners is significant. It is not recommended to collect additional event data when running production workloads.

Attaching and closing execution listeners are expensive operations and typically require to traverse through all loaded code. Code that was previously optimized will be deoptimized in the process. It is most efficient to attach an execution listener before any code is executed and let execution listeners automatically close with the engine.

Compatibility

Event execution order and granularity of events are language specific and may change without notice. There are no compatibility guarantees for that provided by the polyglot SDK. Certain language implementations may do so. Please see the language implementation documentation for further details.

Use-cases

Execution listeners are designed as simple API for polyglot embedders to capture cross cutting concerns of the execution of programs. For example, it can be used to count all statements executed in order to detect potentially malicious code. It is not designed to be an API for implementing fully fledged tools. The Truffle instrumentation framework should be used for that purpose instead.
Since:
19.0
  • Method Details

    • close

      public void close()
      Closes and detaches this execution listener from the engine. After an execution listener was closed no further events will be reported.

      Attaching and closing execution listeners are expensive operations and typically require to traverse through all loaded code. Code that was previously optimized will be deoptimized in the process. It is most efficient to attach an execution listener before any code is executed and let execution listeners automatically close with the engine.

      Specified by:
      close in interface AutoCloseable
      Since:
      19.0
      See Also:
    • newBuilder

      public static ExecutionListener.Builder newBuilder()
      Creates a builder that can be used to attach execution listeners. The returned Builder instance is not thread-safe.

      A minimal example on how to build and attach a listener:

       ExecutionListeners listener = ExecutionListeners.newBuilder()
                .onEnter((e) -> ...)
                .statements(true)
                .attach(context.getEngine());
       
      Since:
      19.0
      See Also: