# Insight for JVM programs

> Trace Java and Kotlin project runs with `elide run --insights`: JavaScript Insight scripts or a Java insight class, method filters, frames, and what differs from Insight on JavaScript.

`elide run --insights=<VALUE>` in a Java or Kotlin project attaches an instrumentation agent to the program's JVM.
The value is either a JavaScript [Insight script](https://elide.help/docs/api-insight) or a Java class:

| Value | Runs | Repeatable |
| --- | --- | --- |
| `trace.js` | A JavaScript script that calls `insight.on` | Yes |
| `class:app.Trace` | A class on the program's classpath with `public static void insightmain(String, JvmInsight)` | No; cannot be mixed with scripts |

`--insights` applies to project runs (`elide run` with a `jvm.main`) and single-file Java or Kotlin runs (`elide run Main.java`).

The examples trace this program:

```pkl
// elide.pkl
amends "elide:project.pkl"

name = "insight-demo"

jvm {
  main = "app.Main"
}
```

```java
// src/main/java/app/Main.java
package app;

public class Main {
  static int add(int a, int b) {
    return a + b;
  }

  public static void main(String[] args) {
    System.out.println(add(1, 2));
  }
}
```

## Scripts

```javascript
// trace.js
insight.on("enter", (ctx, frame) => {
  console.log("enter " + ctx.name + " a=" + frame.a + " b=" + frame.b);
}, { roots: true, rootNameFilter: "Lapp/Main;\\.add.*" });

insight.on("return", (ctx, frame) => {
  console.log("return " + ctx.name);
}, { roots: true, rootNameFilter: "Lapp/Main;\\.add.*" });
```

```bash
elide run --insights=trace.js
```

```text
enter Lapp/Main;.add(II)I a=1 b=2
return Lapp/Main;.add(II)I
3
```

The first run downloads the JavaScript engine for the agent (about 67 MB of JARs) into the Maven cache. JVM warnings and
agent log lines go to stderr.

| Script API | On a JVM program |
| --- | --- |
| Events | `enter` and `return`; no `source` |
| `rootNameFilter` | Required; without it the handler never runs. A regular expression that must match the whole method name, `L<class path>;.<method><descriptor>` |
| `roots`, `statements` | Match method entries, or each statement; no `expressions` |
| `sourceFilter` | Not supported |
| `ctx.name` | The method name in the form above |
| `ctx.line` | Source line for `statements`; `-1` for `roots` |
| `frame` | Arguments and locals by name (`frame.a`) |
| `ctx.returnValue` | Not available |

Handlers see only the program's own classes: a broad filter such as `.*` skips JDK classes and the insight engine itself.

## Classes

A class on the program's classpath receives the agent's API and registers handlers with `onMethod`. The API is in the
bundled `org.apidesign.jvm:jvm-insight:0.2`, which a project declares as `compileOnly`:

```pkl
dependencies {
  maven {
    compileOnly { "org.apidesign.jvm:jvm-insight:0.2" }
  }
}
```

```java
// src/main/java/app/Trace.java
package app;

import org.apidesign.jvm.insight.JvmInsight;

public class Trace {
  public static void insightmain(String args, JvmInsight insight) {
    insight.onMethod((method, builder) -> {
      if (method.clazz().name().equals("app.Main") && method.name().equals("add")) {
        builder.when(JvmInsight.When.ENTER).roots(true)
            .call((at, frame) -> System.out.println("enter " + at.method().name() + " " + frame));
      }
    });
  }
}
```

```bash
elide run --insights=class:app.Trace
```

```text
enter add {a=1, b=2}
3
```

| `JvmInsight` API | Use |
| --- | --- |
| `onMethod((method, builder) -> …)` | Called for each method as its class loads; `method.clazz().name()`, `method.name()`, `method.descriptor()` identify it |
| `builder.when(When.ENTER or When.RETURN)` | When the handler runs |
| `builder.roots(true)`, `builder.statements(true)` | Method entry, or each statement |
| `builder.call((at, frame) -> …)` | The handler; `at.method()`, `at.line()`, and `frame` as a `Map` of arguments and locals |

Class handlers need no download.
