T-Trace: Embedding
T-Trace is a multipurpose, flexible tool providing enourmous possiblilities when it comes to dynamic understanding of user application behavior. See its manual for more details. Read on to learn how to embed T-Trace into your own application.
GraalVM languages can be embedded into custom Java applications via polyglot Context API. T-Trace isn't an exception and it can also be controlled via the same API as well. See AgentScript class documentation for more details:
final Engine engine = context.getEngine();
Instrument instrument = engine.getInstruments().get("agentscript");
Function<Source, AutoCloseable> access = instrument.lookup(Function.class);
AutoCloseable handle = access.apply(agentSrc);
Obtain Engine
for your Context
and ask for agentscript
instrument. Then create
Source
with your T-Trace script and apply it while obtaining
its instrumentation handle. Use handle.close()
to disable all the script's
instrumentations when when no longer needed.
Often one wants to treat certain code written in a dynamic language as a priviledged one - imagine various bindings to OS concepts or other features of one's application. Such scripts are better to remain blackboxed and hidden from T-Trace instrumentation capabilities.
To hide priviledged scripts from T-Trace sight mark such scripts as internal. By default T-Trace ignores and doesn't process internal scripts.
The T-Trace hacker's manual shows many examples of using
T-Trace with node
- however most of them rely on the command
line option --agentscript
and don't benefit from the dynamic nature of
T-Trace much. Let's fix that by showing how to create an
admin server. Define adminserver.js
:
function initializeAgent(agent, 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('agent', data);
try {
fn(agent);
res.write('T-Trace 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) {
agent.off('source', waitForRequire);
initializeAgent(agent, process.mainModule.require);
}
};
agent.on('source', waitForRequire, { roots: true });
which opens an HTTP server at port 9999
and listens for incoming scripts to
be applied any time later. Invoke your application as
$ node --agentscript=adminserver.js --experimental-options yourapp.js
Admin ready at 9999
and while it is running connect to the admin port. Send in any T-Trace script you want.
For example following script is going to observe who calls process.exit
:
$ curl --data \
'agent.on("enter", (ctx, frame) => { console.log(new Error("call to exit").stack); }, \
{ roots: true, rootNameFilter: n => n === "exit" });' \
-X POST http://localhost:9999/
When writing your own adminserver.js
pay attention to security. T-Trace
scripts are very powerful and you want only authorized persons to apply arbitrary
hooks to your application. Don't open the admin server port to everybody.
Read about more T-Trace use-cases in its hacker's manual.