- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
将 Insight 嵌入应用程序
在 Java 中嵌入 Insight #
Graal 语言(使用 Truffle 框架实现的语言,如 JavaScript、Python、Ruby 和 R)可以通过 Polyglot Context API 嵌入到自定义 Java 应用程序中。GraalVM Insight 也可以通过相同的 API 进行控制。例如
final Engine engine = context.getEngine();
Instrument instrument = engine.getInstruments().get("insight");
Function<Source, AutoCloseable> access = instrument.lookup(Function.class);
AutoCloseable handle = access.apply(agentSrc);
获取 Context
的 Engine
并请求 insight
工具。然后使用 GraalVM Insight 脚本创建 Source
,并在获取其检测句柄时应用它。在不再需要时,使用 handle.close()
禁用脚本的所有检测。例如
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);
}
参见 嵌入依赖设置。添加对 insight
的依赖。
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<!-- Select tools: profiler, inspect, coverage, dap, tools -->
<artifactId>insight</artifactId>
<version>23.1.1</version>
<type>pom</type>
</dependency>
忽略内部脚本 #
通常,人们希望将用动态语言编写的某些代码视为特权代码。想象一下各种绑定到操作系统概念或应用程序其他特性的代码。此类脚本最好保持黑盒状态,并对 GraalVM Insight 的检测能力隐藏。
为了隐藏特权脚本,将它们标记为内部。默认情况下,GraalVM Insight 会忽略内部脚本并且不进行处理。
扩展 Insight 脚本的功能 #
将 GraalVM Insight 嵌入到 Java 应用程序中时,您可以让其他对象可供正在评估的 Insight 脚本使用。例如
@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);
}
}
前面的 Java 代码创建了一个工具,它将新符号 meaning
注册到每个评估的 Insight 脚本。每个脚本都可以引用并使用它,例如,限制方法调用的数量
insight.on('enter', (ctx, frames) => { if (--meaning <= 0) throw 'Stop!' }, { roots : true });
可以公开简单值以及复杂对象。有关详细信息,请参阅 javadoc。请注意,检测可以改变程序执行的许多方面,并且不受任何安全沙箱的限制。
将 Insight 嵌入到 Node.js 中 #
在 Insight 手册 中展示了许多将 GraalVM Insight 与 node
结合使用的示例。但是,其中大多数依赖于命令行选项 --insight
,并未充分利用该工具的动态特性。下一个示例展示了如何创建管理服务器。
将此代码保存到 adminserver.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 });
该程序在端口 9999
上打开一个 HTTP 服务器,并监听随时传入的脚本。调用应用程序
node --insight=adminserver.js yourapp.js
Admin ready at 9999
在它运行时,连接到管理端口。向它发送任何 GraalVM Insight 脚本。例如,以下脚本将观察谁调用了 process.exit
curl --data \
'insight.on("enter", (ctx, frame) => { console.log(new Error("call to exit").stack); }, \
{ roots: true, rootNameFilter: "exit" });' \
-X POST https://:9999/
在编写自己的 adminserver.js
时,请注意安全性。只有授权人员才能对您的应用程序应用任意钩子。不要向所有人开放管理服务器端口。
接下来阅读什么 #
要了解有关 Insight 的更多信息并查找一些用例,请访问 Insight 手册。它以一个必不可少的 HelloWorld 示例开始,然后演示更具挑战性的任务。