Experimental feature in GraalVM

Embedding Insight into Applications

Embedding Insight into Java #

Graal languages (languages implemented with the Truffle framework, such as JavaScript, Python, Ruby, and R) can be embedded into custom Java applications via Polyglot Context API. GraalVM Insight can also be controlled via the same API. Like:

final Engine engine = context.getEngine();
Instrument instrument = engine.getInstruments().get("insight");
Function<Source, AutoCloseable> access = instrument.lookup(Function.class);
AutoCloseable handle = access.apply(agentSrc);

Obtain Engine for Context and ask for the insight instrument.

Then create `Source` with the GraalVM Insight script and apply it while obtaining its instrumentation handle. Use `handle.close()` to disable all the script's instrumentation when no longer needed. For Example: ```java Source instrument = Source.create("js", """ insight.on('return', function(ctx, frame) { console.log(`Instrumented where = ${frame.where}`); }, { roots: true, rootNameFilter: 'end', }); """); Source script = Source.create("js", """ function end() { var where = 'end'; console.log(where + ' invoked') } end(); """); try (Context context = Context.newBuilder().build()) { @SuppressWarnings("unchecked") Function<Source, AutoCloseable> insight = context.getEngine().getInstruments().get("insight").lookup(Function.class); // run without instrumentation context.eval(script); // run with instrumentation try (AutoCloseable handle = insight.apply(instrument)) { context.eval(script); } // run without instrumentation context.eval(script); } ``` See [Embedding Dependency Setup](/dev/reference-manual/embed-languages/#dependency-setup). Add a dependency on `insight`: ``` org.graalvm.polyglot insight 23.1.1 pom ``` ### Ignoring Internal Scripts Often one wants to treat certain code written in a dynamic language as a privileged one. Imagine various bindings to OS concepts or other features of one's application. Such scripts are better to remain blackboxed and hidden from GraalVM Insight instrumentation capabilities. To hide privileged scripts from sight, [mark them as internal](https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/Source.Builder.html#internal-boolean-). By default GraalVM Insight ignores and does not process internal scripts. ### Extending Functionality of Insight Scripts When embedding GraalVM Insight into a Java application, you can make additional objects available to the Insight scripts being evaluated. For example: ```java @TruffleInstrument.Registration( id = "meaningOfWorld", name = "Meaning Of World", version = "demo", services = { Insight.SymbolProvider.class } ) public final class MeaningOfWorldInstrument extends TruffleInstrument { @Override protected void onCreate(Env env) { Map<String, Integer> symbols = Collections.singletonMap("meaning", 42); Insight.SymbolProvider provider = () -> symbols; env.registerService(provider); } } ``` The previous Java code creates an instrument which registers a new symbol `meaning` to every Insight script evaluated. Each script can then reference it and use it, for example, to limit the number of method invocations: ```java insight.on('enter', (ctx, frames) => { if (--meaning <= 0) throw 'Stop!' }, { roots : true }); ``` It is possible to expose simple values, as well as complex objects. See the [javadoc](https://www.graalvm.org/tools/javadoc/org/graalvm/tools/insight/Insight.SymbolProvider.html) for more detailed information. Note that instrumentation can alter many aspects of program execution and are not subject to any security sandbox. ## Embedding Insight into Node.js The [Insight Manual](/dev/tools/graalvm-insight/manual/) shows many examples of using GraalVM Insight with `node`. However most of them rely on the command line option `--insight` and do not benefit from the dynamic nature of the tool. The next example shows how to create an admin server. Save this code to `adminserver.js`: ```js function initialize(insight, require) { const http = require("http"); const srv = http.createServer((req, res) => { let method = req.method; if (method === 'POST') { var data = ''; req.on('data', (chunk) => { data += chunk.toString(); }); req.on('end', () => { const fn = new Function('insight', data); try { fn(insight); res.write('GraalVM Insight hook activated\n'); } finally { res.end(); } }); } }); srv.listen(9999, () => console.log("Admin ready at 9999")); } let waitForRequire = function (event) { if (typeof process === 'object' && process.mainModule && process.mainModule.require) { insight.off('source', waitForRequire); initialize(insight, process.mainModule.require.bind(process.mainModule)); } }; insight.on('source', waitForRequire, { roots: true }); ``` The program opens an HTTP server at port `9999` and listens for incoming scripts to be applied any time later. Invoke the application: ```bash node --insight=adminserver.js yourapp.js Admin ready at 9999 ``` While it is running, connect to the admin port. Send in any GraalVM Insight script to it. For example, the following script is going to observe who calls `process.exit`: ```bash curl --data \ 'insight.on("enter", (ctx, frame) => { console.log(new Error("call to exit").stack); }, \ { roots: true, rootNameFilter: "exit" });' \ -X POST http://localhost:9999/ ``` When writing your own `adminserver.js`, pay attention to security. Only an authorized person should apply arbitrary hooks to your application. Do not open the admin server port to everybody. ### What to Read Next To learn more about Insight and find some use cases, go to the [Insight Manual](/dev/tools/graalvm-insight/manual/). It starts with an obligatory _HelloWorld_ example and then demonstrates more challenging tasks.

Connect with us