{"id":48904589,"url":"https://github.com/sfkamath/jvm-hotpath","last_synced_at":"2026-04-19T10:01:15.306Z","repository":{"id":336007960,"uuid":"1146992399","full_name":"sfkamath/jvm-hotpath","owner":"sfkamath","description":"JVM runtime execution analysis - finds hot code paths by frequency, not CPU time","archived":false,"fork":false,"pushed_at":"2026-03-28T18:41:56.000Z","size":988,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-28T19:28:22.150Z","etag":null,"topics":["asm","bytecode","code-coverage","execution-counts","java","java-agent","jvm","maven-plugin","runtime-analysis"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sfkamath.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-01T02:26:05.000Z","updated_at":"2026-03-28T18:38:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sfkamath/jvm-hotpath","commit_stats":null,"previous_names":["sfkamath/jvm-hotpath"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/sfkamath/jvm-hotpath","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfkamath%2Fjvm-hotpath","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfkamath%2Fjvm-hotpath/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfkamath%2Fjvm-hotpath/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfkamath%2Fjvm-hotpath/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sfkamath","download_url":"https://codeload.github.com/sfkamath/jvm-hotpath/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfkamath%2Fjvm-hotpath/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32002361,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"online","status_checked_at":"2026-04-19T02:00:07.110Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asm","bytecode","code-coverage","execution-counts","java","java-agent","jvm","maven-plugin","runtime-analysis"],"created_at":"2026-04-16T19:00:43.474Z","updated_at":"2026-04-19T10:01:15.234Z","avatar_url":"https://github.com/sfkamath.png","language":"Java","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"agent/src/main/resources/io/github/sfkamath/jvmhotpath/favicon.png\" width=\"200\" alt=\"JVM Hotpath Logo\"\u003e\n\u003c/p\u003e\n\n# JVM Hotpath Agent\n\n[![Java CI](https://github.com/sfkamath/jvm-hotpath/actions/workflows/ci.yml/badge.svg)](https://github.com/sfkamath/jvm-hotpath/actions/workflows/ci.yml)\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.sfkamath/jvm-hotpath-agent.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.sfkamath%22%20AND%20a:%22jvm-hotpath-agent%22)\n[![Gradle Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/io.github.sfkamath.jvm-hotpath)](https://plugins.gradle.org/plugin/io.github.sfkamath.jvm-hotpath)\n[![Version](https://img.shields.io/github/v/tag/sfkamath/jvm-hotpath)](https://github.com/sfkamath/jvm-hotpath/tags)\n[![License](https://img.shields.io/github/license/sfkamath/jvm-hotpath)](LICENSE)\n\nA Java agent that instruments classes at runtime to record and visualize line-level execution counts. It generates a modern, interactive HTML report with a file tree, global heatmap, and support for both dark and light modes.\n\n\n## Features\n\n- **Bytecode Instrumentation**: Automatically injects counting logic into target methods using ASM.\n- **Frequency Analysis**: Tracks exactly how many times each line executes, rather than just \"if\" it was hit.\n- **Modern UI**: Interactive report built with Vue.js 3 and PrismJS.\n- **Live Updates**: Uses JSONP + polling for \"serverless\" real-time updates, so you can watch counts increase while the app runs (even from `file://`).\n- **Global Heatmap**: Consistent coloring across all source files based on the project-wide maximum execution count.\n- **Activity Highlighting**: Visual \"flash\" indicators in the file tree when counts for a specific file increase.\n- **Standalone Mode**: Regenerate the HTML report from saved JSON data without re-running the application.\n\n## Motivation\n\nJVM Hotpath is **not a coverage tool**. Coverage tools (e.g., JaCoCo, OpenClover, JCov) are designed around coverage (did it execute), not frequency (how many times did it execute). That's critical for quality metrics, but limited for understanding **runtime behavior** and **hot-path analysis**.\n\nJVM Hotpath focuses on frequency: \"*How many times does this line execute in a real-world workload?*\"\n\nSee [docs/Motivation.md](docs/Motivation.md) for a more detailed deep-dive into the goals and architectural choices of this project.\n\nIDEs do not expose an easy way to visualize per-line execution as the app runs. JVM Hotpath bridges that gap by instrumenting production-like workloads, streaming live frequency data to a local HTML UI, and surfacing hotspots without needing a server or sacrificing compatibility.\n\n## Why Traditional Profilers Miss This\n\nIn the era of **vibe coding**, where large amounts of code are introduced or refactored in short bursts (often with the help of LLMs), traditional profiling workflows can feel too heavy. Attaching a commercial profiler, configuring sampling rates, and navigating complex call trees for every small logic change is a significant friction point. \n\nI found the need for a \"low-ceremony\" way to verify that new code behaves as expected. When you're moving fast, you don't always need a nanosecond-precise timing breakdown; you need an immediate, visual confirmation that your loops aren't spinning 10,000x more than they should. JVM Hotpath was built to be that lightweight \"Logic X-Ray\" that stays out of your way until it finds a logic error.\n\n### The Real-World Case Study\n\nThis tool was born during a high-velocity \"vibe coding\" session where I was refactoring a core processing engine. With hundreds of lines changing at once, I needed to know if my architectural \"vibes\" matched the actual runtime reality. \n\nStandard profilers missed the following bug because the system didn't *feel* slow yet, but the logic was fundamentally broken:\n\n**The Bug:** A logic check (e.g., `isValid()`) was being called 19 million times in 15 seconds.  \n**The Problem:** Each call was ~50 nanoseconds - easy for sampling profilers to under-sample.  \n**The Impact:** Algorithmic complexity (O(N) instead of O(1)) was killing performance.\n\nStandard profilers showed the method as \"not hot\" because the CPU wasn't stuck there. But 19 million calls × 50ns = 950ms of wasted time hidden in plain sight.\n\n### How Current Tools Fall Short\n\n| Tool Type | What It Shows | What It Misses |\n|-----------|---------------|----------------|\n| **Sampling Profilers**\u003cbr/\u003e(VisualVM, JFR) | CPU-intensive methods | Fast methods called millions of times |\n| **Commercial Profilers**\u003cbr/\u003e(JProfiler, YourKit) | Deep timing and call tracing | Always-on convenience (heavier workflow, and instrumentation/tracing can add noticeable overhead) |\n| **APM Tools**\u003cbr/\u003e(Datadog, New Relic) | Request/span-level metrics | Line-level logic errors |\n\n### The Key Insight: Frequency ≠ Duration\n\nJava profilers focus on **where the CPU is hot** (timing).  \nThis tool shows **how many times code runs** (frequency).\n\nIn modern Java:\n- JIT compilation makes methods fast\n- The bottleneck is often algorithmic (O(N) vs O(1))\n- Logic errors create millions of unnecessary calls\n- Sampling profilers are statistical: they do not provide exact invocation counts, and very short \"fast but frequent\" work can be under-sampled\n\n**Example:**\n```\nSampler says: \"Line 96 uses 2.3% CPU time\"\nHotpath says: \"Line 96: executed 19,147,293 times\"\n```\n\nOne is a performance metric. The other is a logic error screaming at you.\n\n### What This Tool Does Differently\n\n✅ **Zero timing overhead** - Just counts, no nanosecond measurements  \n✅ **Counts every execution** - No sampling, no missing fast methods  \n✅ **Simple output** - JSON/HTML, not a heavy GUI  \n✅ **LLM-friendly** - Pipe the report to Claude/GPT for analysis  \n✅ **Logic-focused** - Finds algorithmic problems, not just CPU hotspots  \n\n**It's a \"Logic X-Ray\" not a \"CPU Thermometer\".**\n\n![JVM Hotpath Gson Demo](https://github.com/user-attachments/assets/cc89451b-a41f-491e-a1f6-8e87328979c0)\n\nWhen you see \"Line 42: executed 19 million times\" in a 15-second run, you don't need to measure nanoseconds. You need to fix your algorithm.\n\n## Requirements\n\n- **Java:** 11 or higher (tested in CI on 11, 17, 21, 23, and 24)\n- **Build Tool:** Maven 3.6+ or Gradle 7.0+\n\nThe agent is compiled to Java 11 bytecode for maximum compatibility. Java 25 is currently blocked by upstream bytecode-tooling support (see Development section).\n\n## Quick Start\n\n### Gradle Plugin (Recommended for Gradle Users)\n\nAdd the plugin to your build:\n\nKotlin DSL (`build.gradle.kts`):\n\n```kotlin\nplugins {\n    java\n    id(\"io.github.sfkamath.jvm-hotpath\") version \"0.2.10\"\n}\n\njvmHotpath {\n    packages.set(\"com.example\")\n    flushInterval.set(5)\n}\n```\n\nGroovy DSL (`build.gradle`):\n\n```groovy\nplugins {\n    id 'java'\n    id 'io.github.sfkamath.jvm-hotpath' version '0.2.10'\n}\n\njvmHotpath {\n    packages.set('com.example')\n    flushInterval.set(5)\n}\n```\n\nThen run your application:\n\n```bash\n./gradlew run\n```\n\nReport output: `target/site/jvm-hotpath/execution-report.html`\n\n### Maven Plugin (Recommended for Maven Users)\n\nAdd this `instrument` profile to your `pom.xml`:\n\n```xml\n\u003cprofile\u003e\n    \u003cid\u003einstrument\u003c/id\u003e\n    \u003cbuild\u003e\n        \u003cplugins\u003e\n            \u003cplugin\u003e\n                \u003cgroupId\u003eio.github.sfkamath\u003c/groupId\u003e\n                \u003cartifactId\u003ejvm-hotpath-maven-plugin\u003c/artifactId\u003e\n                \u003cversion\u003e0.2.10\u003c/version\u003e\n                \u003cexecutions\u003e\n                    \u003cexecution\u003e\n                        \u003cgoals\u003e\n                            \u003cgoal\u003eprepare-agent\u003c/goal\u003e\n                        \u003c/goals\u003e\n                    \u003c/execution\u003e\n                \u003c/executions\u003e\n                \u003cconfiguration\u003e\n                    \u003cflushInterval\u003e5\u003c/flushInterval\u003e\n                \u003c/configuration\u003e\n            \u003c/plugin\u003e\n            \u003cplugin\u003e\n                \u003cgroupId\u003eorg.codehaus.mojo\u003c/groupId\u003e\n                \u003cartifactId\u003eexec-maven-plugin\u003c/artifactId\u003e\n                \u003cversion\u003e3.5.0\u003c/version\u003e\n                \u003cconfiguration\u003e\n                    \u003cexecutable\u003ejava\u003c/executable\u003e\n                    \u003ccommandlineArgs\u003e${jvmHotpathAgentArg} -classpath %classpath ${exec.mainClass}\u003c/commandlineArgs\u003e\n                \u003c/configuration\u003e\n            \u003c/plugin\u003e\n        \u003c/plugins\u003e\n    \u003c/build\u003e\n\u003c/profile\u003e\n```\n\nRun your app:\n\n```bash\nmvn -Pinstrument jvm-hotpath:prepare-agent exec:exec\n```\n\n\u003e **Note:** `exec:exec` requires a main class. Provide it via `-Dexec.mainClass=...`, or configure `mainClass`/`exec.mainClass` in your `pom.xml`.\n\nReport output: `target/site/jvm-hotpath/execution-report.html`\n\n### Direct `-javaagent` (Alternative)\n\nRun the app with `-javaagent` to instrument without the Maven plugin. For more details, see [Manual `-javaagent` Workflow](#manual-javaagent-workflow).\n\n## Workflows\n\nChoose either the Gradle plugin, Maven plugin, or manual `-javaagent` workflow.\n\n### Gradle Plugin Workflow\n\nApply the plugin as shown in Quick Start. The plugin automatically:\n- Attaches the agent to all `JavaExec` tasks (including `run`, `bootRun`, etc.)\n- Collects source paths from the `main` source set across all subprojects\n- Seeds `packages` with your project's `groupId`\n\nBy default, test tasks are not instrumented. To include tests, set `instrumentTests` to `true`.\n\nCommon Gradle plugin usage:\n\nKotlin DSL:\n\n```kotlin\njvmHotpath {\n    packages.set(\"com.example,com.other.module\")\n    exclude.set(\"com.example.generated.*\")\n    flushInterval.set(5)\n    output.set(layout.buildDirectory.file(\"site/jvm-hotpath/execution-report.html\").get().asFile.path)\n    sourcepath.set(\"module-a/src/main/java:module-a/target/generated-sources\")\n    verbose.set(true)\n    keepAlive.set(false)\n    append.set(true)\n    instrumentTests.set(true)\n    skip.set(false)\n}\n```\n\nGroovy DSL:\n\n```groovy\njvmHotpath {\n    packages.set('com.example,com.other.module')\n    exclude.set('com.example.generated.*')\n    flushInterval.set(5)\n    output.set(\"${layout.buildDirectory.get()}/site/jvm-hotpath/execution-report.html\")\n    sourcepath.set('module-a/src/main/java:module-a/target/generated-sources')\n    verbose.set(true)\n    keepAlive.set(false)\n    append.set(true)\n    instrumentTests.set(true)\n    skip.set(false)\n}\n```\n\n\u003ca id=\"jmh-integration\"\u003e\u003c/a\u003e\n### JMH Integration (Gradle)\n\nJMH spawns isolated forked JVMs for each benchmark iteration. The jvm-hotpath plugin does not auto-configure these forks — you need to inject the agent manually into the `jmh` task's `jvmArgsAppend`.\n\n\u003e **Note:** If your JMH setup uses an ASM version too old for your JDK, the plugin will warn at configuration time. See [Resolving the ASM version conflict](#resolving-the-asm-version-conflict) below.\n\n**Step 1 — Inject the agent into JMH forks**\n\nAdd the following to your `build.gradle` (Groovy DSL). The agent JAR is available via the `jvmHotpathAgent` configuration that the plugin creates:\n\n```groovy\nafterEvaluate {\n    def agentJar = configurations.jvmHotpathAgent.files\n        .find { it.name.startsWith('jvm-hotpath-agent') \u0026\u0026 it.name.endsWith('.jar') }\n        .absolutePath\n    def args = [\n        \"packages=com.example\",\n        \"sourcepath=src/main/java\",\n        \"append=true\",\n        \"flushInterval=1\",\n        \"output=${layout.buildDirectory.get().asFile.absolutePath}/jvm-hotpath/execution-report.html\"\n    ].join(',')\n    tasks.named(\"jmh\").configure {\n        jvmArgsAppend = [\"-javaagent:${agentJar}=${args}\"]\n    }\n}\n```\n\nTwo settings are critical for JMH forks:\n- **`append=true`** — each fork appends its counts to the same file rather than overwriting it\n- **`flushInterval=1`** — flushes data to disk every second, ensuring counts survive when the fork is killed after each iteration\n\n\u003ca id=\"resolving-the-asm-version-conflict\"\u003e\u003c/a\u003e\n**Step 2 — Resolve the ASM version conflict**\n\nJMH bundles its own (older) copy of ASM. When the jvm-hotpath agent attempts to instrument Java 17+ bytecode inside a fork, it picks up JMH's ASM and crashes with `Unsupported class file major version`. Fix this by forcing a newer ASM version into the `jmh` dependency configuration:\n\n```groovy\ndependencies {\n    jmh 'org.ow2.asm:asm:9.9.1'\n    jmh 'org.ow2.asm:asm-tree:9.9.1'\n    jmh 'org.ow2.asm:asm-analysis:9.9.1'\n    jmh 'org.ow2.asm:asm-commons:9.9.1'\n    jmh 'org.ow2.asm:asm-util:9.9.1'\n}\n```\n\n**Step 3 — Analyse the results**\n\nBecause JMH runs many iterations across many forks, the HTML report can be large. Use the `jq` query from [CLI Analysis with jq](#cli-analysis-with-jq) to find the hottest lines directly from the terminal.\n\n### Maven Plugin Workflow\n\nUse the `instrument` profile from Quick Start.\nThis workflow requires `exec:exec` to have a main class (via `-Dexec.mainClass=...` or config).\n\nFor `exec:exec`, `prepare-agent` resolves `exec.mainClass` in this order:\n1. `-Dexec.mainClass`\n2. `jvm-hotpath.mainClass`\n3. `main.class`\n4. `mainClass`\n5. `start-class`\n6. `spring-boot.run.main-class`\n\nIf no main class can be resolved, `prepare-agent` fails fast. This validation is skipped for non-`exec` runs (for example test-only runs).\n\nCommon Maven plugin extensions:\n\n#### Instrument Tests\n\nBy default, tests are not instrumented. To include tests (Surefire/Failsafe), set:\n\n```bash\nmvn -Djvm-hotpath.instrumentTests=true ...\n```\n\nWhen tests are not instrumented, the plugin sets the agent string into `jvmHotpathAgentArg` instead of `argLine`.\n\n#### Add More Packages or Source Roots\n\nUse this when your run spans multiple modules or generated sources:\n\n```xml\n\u003cconfiguration\u003e\n    \u003cpackages\u003ecom.example,com.other.module\u003c/packages\u003e\n    \u003csourcepath\u003e\n        module-a/src/main/java:module-a/target/generated-sources:module-b/src/main/java\n    \u003c/sourcepath\u003e\n\u003c/configuration\u003e\n```\n\nOr as a one-off CLI override:\n\n```bash\nmvn -Pinstrument jvm-hotpath:prepare-agent exec:exec \\\n  -Djvm-hotpath.packages=com.example,com.other.module \\\n  -Djvm-hotpath.sourcepath=module-a/src/main/java:module-a/target/generated-sources:module-b/src/main/java\n```\n\nUse `:` on macOS/Linux and `;` on Windows for `sourcepath`.\n\n#### Include Dependency Sources\n\nUse `\u003cincludes\u003e` in `pom.xml` for repeatable team/project config:\n\n```xml\n\u003cconfiguration\u003e\n    \u003cpackages\u003ecom.legacy.utils\u003c/packages\u003e\n    \u003cincludes\u003e\n        \u003cinclude\u003e\n            \u003cgroupId\u003ecom.example\u003c/groupId\u003e\n            \u003cartifactId\u003eshared-library\u003c/artifactId\u003e\n            \u003cpackageName\u003ecom.example.shared\u003c/packageName\u003e\n        \u003c/include\u003e\n    \u003c/includes\u003e\n\u003c/configuration\u003e\n```\n\nUse CLI override only for one-off local runs:\n\n```bash\nmvn -Pinstrument jvm-hotpath:prepare-agent exec:exec \\\n  -Djvm-hotpath.packages=com.example.shared \\\n  -Djvm-hotpath.sourcepath=$HOME/.m2/repository/com/example/shared-library/1.0.0/shared-library-1.0.0-sources.jar\n```\n\n`sourcepath` accepts directories and source archives (`.jar`/`.zip`). If you provide archives manually, match source and runtime versions.\n\n#### Additive Mode (Accumulation)\n\nBy default, every run overwrites the previous report. Use `append` to accumulate counts across multiple JVM runs. This is useful for complex applications where multiple distinct user journeys, batch jobs, or manual workloads need to be combined to see the full hot-path picture.\n\n```xml\n\u003cconfiguration\u003e\n    \u003cappend\u003etrue\u003c/append\u003e\n\u003c/configuration\u003e\n```\n\n**Drift Detection (Filesystem-as-Truth):**\nTo ensure data integrity, the agent calculates a CRC32 checksum for every source file. During an `append` run:\n1. It compares the current source checksum with the one stored in the existing report.\n2. **If they match:** The previous counts are rehydrated and added to the current session.\n3. **If they differ:** The source has changed (line numbers may have shifted). The agent logs a `WARNING` and ignores previous counts for that specific file to avoid misleading reports.\n\n\u003ca id=\"manual-javaagent-workflow\"\u003e\u003c/a\u003e\n### Manual `-javaagent` Workflow\n\nDownload the agent from Maven Central:\n\n```bash\nwget https://repo1.maven.org/maven2/io/github/sfkamath/jvm-hotpath-agent/0.2.10/jvm-hotpath-agent-0.2.10.jar\nexport PATH_TO_AGENT_JAR=\"$PWD/jvm-hotpath-agent-0.2.10.jar\"\n```\n\nOr build locally:\n\n```bash\nmvn clean package -DskipTests\nexport PATH_TO_AGENT_JAR=\"$PWD/agent/target/jvm-hotpath-agent-0.2.10.jar\"\n```\n\nRun with single-source config:\n\n```bash\njava -javaagent:${PATH_TO_AGENT_JAR}=packages=com.example,sourcepath=src/main/java,flushInterval=5,output=target/site/jvm-hotpath/execution-report.html -jar your-app.jar\n```\n\nReport output: `target/site/jvm-hotpath/execution-report.html`\n\nRun with multi-source config:\n\n```bash\nAGENT_ARGS=\"packages=com.example,com.other.module,flushInterval=5,output=target/site/jvm-hotpath/execution-report.html,sourcepath=module-a/src/main/java:module-a/target/generated-sources:module-b/src/main/java\"\njava -javaagent:${PATH_TO_AGENT_JAR}=\"${AGENT_ARGS}\" -jar your-app.jar\n```\n\nUse `:` on macOS/Linux and `;` on Windows for `sourcepath`.\n\n### Configuration Options\n\nSmart defaults (plugin workflow):\n\n- `packages`: starts with your project `groupId`\n- `sourcepath`: starts with compile source roots (typically `src/main/java`)\n- `output`: defaults to `target/site/jvm-hotpath/execution-report.html`\n- `flushInterval`: defaults to `0` (set to `5` for live updates)\n\nUse the table below as the full reference.\n\n| Option | Scope | Agent Arg | Maven Plugin Config | Notes |\n| :--- | :--- | :--- | :--- | :--- |\n| `packages` | Agent + Plugin | `packages=` | `jvm-hotpath.packages` / `\u003cpackages\u003e` | Plugin seeds with project `groupId`, then appends configured values. |\n| `exclude` | Agent + Plugin | `exclude=` | `jvm-hotpath.exclude` / `\u003cexclude\u003e` | Exclusion list passed through to agent. |\n| `flushInterval` | Agent + Plugin | `flushInterval=` | `jvm-hotpath.flushInterval` / `\u003cflushInterval\u003e` | Interval in seconds. Default `0` (no periodic flush). |\n| `output` | Agent + Plugin | `output=` | `jvm-hotpath.output` / `\u003coutput\u003e` | Default `target/site/jvm-hotpath/execution-report.html`. |\n| `sourcepath` | Agent + Plugin | `sourcepath=` | `jvm-hotpath.sourcepath` / `\u003csourcepath\u003e` | Supports directories and source archives (`.jar`/`.zip`). |\n| `verbose` | Agent + Plugin | `verbose=` | `jvm-hotpath.verbose` / `\u003cverbose\u003e` | Extra instrumentation/flush logging. |\n| `keepAlive` | Agent + Plugin | `keepAlive=` | `jvm-hotpath.keepAlive` / `\u003ckeepAlive\u003e` | Agent default is `true`; plugin emits when enabled. |\n| `append` | Agent + Plugin | `append=` | `jvm-hotpath.append` / `\u003cappend\u003e` | If `true`, loads existing report counts at startup to accumulate across runs. |\n| `instrumentTests` | Plugin only | n/a | `jvm-hotpath.instrumentTests` / `\u003cinstrumentTests\u003e` | Attach the agent to test tasks. Default `false`. |\n| `mainClass` | Plugin only | n/a | `jvm-hotpath.mainClass` / `\u003cmainClass\u003e` | Populates `exec.mainClass` for `exec:exec`. |\n| `includes` | Plugin only | n/a | `\u003cincludes\u003e` | Resolves dependency sources and appends them to `sourcepath`. |\n| `propertyName` | Plugin only | n/a | `jvm-hotpath.propertyName` / `\u003cpropertyName\u003e` | Target property for injected `-javaagent` string (default `argLine`, or `jvmHotpathAgentArg` when `instrumentTests=false`). |\n| `skip` | Plugin only | n/a | `jvm-hotpath.skip` / `\u003cskip\u003e` | Skips plugin execution. |\n\n## Report and Output\n\n### Viewing the Report\n\n1.  Open the generated `target/site/jvm-hotpath/execution-report.html` file in any modern web browser.\n2.  If `flushInterval` is set, the report will automatically poll for updates from a sibling `execution-report.js` file.\n3.  **No Web Server Required**: Thanks to the JSONP implementation, live updates work even when the file is opened directly from disk (`file://` protocol).\n4.  If you open the report from disk and nothing renders, hard-refresh once (the `report-app.js` bundle is copied alongside the report and may be cached).\n\n### Report Artifacts\n\nThe agent produces both human-readable and machine-readable output in the `target/site/jvm-hotpath/` directory:\n\n#### Primary Outputs\n- **`execution-report.html`**: The interactive web UI for developers. Self-contained with the initial data snapshot.\n- **`execution-report.json`**: Pure JSON data for machine consumption (CI pipelines, LLM analysis, etc.).\n\n#### Supporting Assets\n- **`execution-report.js`**: A JSONP wrapper used by the HTML report for live updates without a web server.\n- **`report-app.js`**: The bundled Vue.js runtime used by the HTML UI.\n\nThe JSON payload format is optimized for clarity:\n\n```json\n{\n  \"generatedAt\": 1700000000000,\n  \"files\": [\n    {\n      \"path\": \"com/example/Foo.java\", \n      \"project\": \"my-module\",\n      \"counts\": { \"12\": 3, \"13\": 47293 }, \n      \"content\": \"...\" \n    }\n  ]\n}\n```\n\nSee `docs/jsonp-live-updates.md` for implementation details and gotchas.\n\n### Standalone Report Generation\n\nIf you have a saved `execution-report.json` file and want to regenerate the HTML UI (e.g., after updating the template or changing themes):\n\n```bash\njava -jar ${PATH_TO_AGENT_JAR} --data=target/site/jvm-hotpath/execution-report.json --output=target/site/jvm-hotpath/new-report.html\n```\n\n\u003ca id=\"cli-analysis-with-jq\"\u003e\u003c/a\u003e\n### CLI Analysis with jq\n\nThe `execution-report.json` is machine-readable and works well with `jq` for instant terminal-based analysis — useful when the HTML report is large (e.g. after a JMH run with many benchmark forks) or when you want to script hotspot detection in CI.\n\n**Top 20 hottest lines across all files:**\n\n```bash\njq '[.files[] | select((.counts | length) \u003e 0) | {path: .path, counts: .counts | to_entries} | .counts[] as $c | {file: .path, line: $c.key|tonumber, hits: $c.value}] | sort_by(.hits) | reverse | .[0:20]' path/to/execution-report.json\n```\n\nExample output:\n\n```json\n[\n  { \"file\": \"com/example/OrderService.java\", \"line\": 42, \"hits\": 19147293 },\n  { \"file\": \"com/example/OrderService.java\", \"line\": 43, \"hits\": 19147293 },\n  ...\n]\n```\n\n**How the query works:**\n\n| Step | Expression | Purpose |\n|------|-----------|---------|\n| 1 | `.files[] \\| select((.counts \\| length) \u003e 0)` | Skips files with no recorded executions |\n| 2 | `{path: .path, counts: .counts \\| to_entries}` | Converts the `{\"lineNumber\": hitCount}` map into a `[{key, value}]` array |\n| 3 | `.counts[] as $c \\| {file: .path, line: $c.key\\|tonumber, hits: $c.value}` | Flattens to one object per line |\n| 4 | `sort_by(.hits) \\| reverse` | Hottest lines first |\n| 5 | `.[0:20]` | Top 20 only |\n\n\u003e **Tip:** Run `./gradlew hotpathHelp` to print this query with your project's report path.\n\n## Development\n\n- **Development JDK:** Java 21\n- **Bytecode Target:** Java 11 (for maximum runtime compatibility)\n- **Instrumentation Engine:** ASM 9.9.1 (supports up to Java 24 bytecode)\n- **CI Testing Matrix:** Covers Java 11, 17, 21, 23 and 24.\n\nTo build the agent JAR (shaded with all dependencies):\n\n```bash\nmvn clean package -DskipTests\n```\n\nThe resulting JAR will be at `agent/target/jvm-hotpath-agent-0.2.10.jar`.\n\n\u003e **Frontend build:** The report UI lives in `report-ui/` and is bundled via Vite. `mvn clean package` runs `frontend-maven-plugin` to execute `npm install`/`npm run build` inside that folder before packaging, producing a browser-safe `report-app.js` (IIFE bundle). When iterating on the UI you can run `npm install \u0026\u0026 npm run build` manually from `report-ui/` to refresh the bundled asset.\n\n\u003e **Java 25 Note:** Support for Java 25 is currently blocked until the ASM project releases a version that supports the finalized Java 25 bytecode specification. Using the agent on a Java 25 JVM will likely result in an `UnsupportedClassVersionError` during instrumentation.\n\n## Internal Safety Mechanisms\n\n- **Filesystem-as-Truth Filtering**: The agent only instruments classes if their corresponding `.java` source file is found in the `sourcepath`. This automatically excludes standard libraries, third-party dependencies, and test frameworks (JUnit, Mockito) without manual configuration.\n- **Infrastructure Exclusions**: Core framework classes (e.g., `io.micronaut`, `io.netty`) and generated proxy classes (e.g., `$Definition`, `$$EnhancerBySpring`) are automatically excluded to prevent interference with application lifecycles.\n- **Non-Daemon Threads**: The agent starts a non-daemon \"heartbeat\" thread (configurable via `keepAlive`) to ensure the JVM stays alive for monitoring even if the application's main thread completes.\n- **Robustness**: Instrumentation is wrapped in `Throwable` blocks to prevent bytecode errors from crashing the application.\n\n## Contributing\n\nWe built this because we needed it. If you need it too, let's make it better together.\n\n- 🐛 Found a bug? [Open an issue](https://github.com/sfkamath/jvm-hotpath/issues)\n- 💡 Have an idea? [Start a discussion](https://github.com/sfkamath/jvm-hotpath/discussions)\n- 🔧 Want to contribute? [Submit a PR](https://github.com/sfkamath/jvm-hotpath/pulls)\n\n## License\n\n[MIT License](LICENSE) - Free to use, modify, and distribute.\n","funding_links":[],"categories":["Projects"],"sub_categories":["Performance analysis"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsfkamath%2Fjvm-hotpath","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsfkamath%2Fjvm-hotpath","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsfkamath%2Fjvm-hotpath/lists"}