Greetings, Please review this change to improve JFR’s reporting of JVMTI agents and, for better separation of concerns, the responsibilities and lifetimes of JVMTI agents.
## Problem Tools commonly reuse `Agent_OnAttach` as a control channel—for example, to start and stop profiling. Previously, each successful startup invocation was counted as a new agent instance, producing duplicate and misleading JFR reporting. This change associates reporting with JVMTI environment lifetimes established during agent startup, while separating those lifetimes from library lookup and `Agent_OnUnload` notifications. For further details and a before-and-after profiling demonstration, see [JDK-8392584](https://bugs.openjdk.org/browse/JDK-8392584). ## Model and responsibilities The model separates agent startup from loading its underlying OS library. An agent startup invocation of an entry point may establish a new registered agent instance or simply issue a command to an already existing agent. The responsibilities are divided as follows: - `JvmtiAgent` represents a startup invocation and holds its identity, JVMTI environment association, startup state, and initialization timing. - `JvmtiAgentSet` manages startup attribution, JVMTI environment association and disposal, and the append-only registry of registered JvmtiAgents. - `JvmtiResolver` represents shared library-resolution identity and manages native-lookup eligibility and `Agent_OnUnload` registration. - `JvmtiAgentLifecycle` handles OS library loading, agent startup entry-point resolution, invocation of startup entry-points, and unload notifications. Dynamically loaded OS libraries are identified by their OS handles. Statically linked JvmtiAgents are distinguished by normalized names. ## JVMTI environment association and retention The first successfully created JVMTI environment on the startup thread is associated with the innermost executing startup invocation. This environment becomes the `JvmtiAgent`'s identity over its lifetime. The association has some constraints: - Disposing the associated environment does not make the `JvmtiAgent` eligible for reassociation. - Additional environments created by an inner startup invocation do not fall through to an outer invocation. - Environment creation outside a startup invocation issued by the JVM does not establish a tracked `JvmtiAgent` instance. This direct association replaces attribution through callback addresses and hardcoded knowledge of the JPLIS environment-local-storage layout. The decision to retain or discard a `JvmtiAgent` depends on whether the associated environment remains live after startup completion, regardless of the startup return code. A failed `Agent_OnAttach` invocation can therefore leave a retained `JvmtiAgent` instance, while a successful control command carried by a `JvmtiAgent` without a new JVMTI environment is not retained. Accordingly, the boolean returned by `JvmtiAgentSet::startup` means **retain this instance**, rather than **startup succeeded**. ## JFR events The `jdk.JavaAgent` and `jdk.NativeAgent` events gain a `disposalTime` field. `JvmtiEnv` disposal complements periodic event emitting of live agents, including cases where a JVMTI environment is created and disposed within one startup invocation. Disposal event emission may be deferred until the startup entry point or the `VMInit` callback returns, so that initialization timing is complete; the disposal timestamp records when disposal is emitted, not necessarily when the JVMTI environment was disposed. The change also fixes incorrect native-agent library paths caused by stale contents in a shared path buffer. ## Native lookup and Agent_OnUnload notifications Native-lookup eligibility and `Agent_OnUnload` registration are managed independently of registered `JvmtiAgent` lifetimes: - Successful `JvmtiAgent` startup enables native lookup, independently of JVMTI environment creation, in first-successful-startup order. - Later startup failure or JVMTI environment disposal does not revoke native lookup. - Returning from `Agent_OnLoad` or `Agent_OnAttach`, including their statically linked variants, registers the associated `JvmtiResolver` to be notified via `Agent_OnUnload`. This includes failed `Agent_OnAttach` invocations. If a specific startup entry point is not found in the corresponding OS library, that `JvmtiResolver` instance is not registered for `Agent_OnUnload`. - During VM shutdown, the JVM resolves and invokes the registered `Agent_OnUnload` entry points, at most once per registered resolver, provided the entry point exists. - Unconverted `-Xrun` agents retain previous behavior: `JVM_OnLoad` startup does not register them for native lookup or `Agent_OnUnload`. Loaded OS libraries remain resident for the VM lifetime, including after failed `Agent_OnAttach` invocations. This changes the previous failure behavior because third-party library code may already have started native threads, making unloading unsafe. ## Concurrency and ownership `JvmtiAgent` registration, JVMTI environment association and disposal, and interning of `JvmtiResolver` s are all guarded by the `JvmtiThreadState_lock`, except during the single-threaded VM bootstrap `JVMTI_PHASE_ONLOAD` phase. Registered `JvmtiAgent`s are never removed, even if their JVMTI environment is disposed; this enables lock-free traversal. `JvmtiAgent` startup, JVMTI environment association, and initialization timing have separate states. ## Suggested review order 1. Start with `JvmtiAgent` and its state model. Then review how JvmtiAgents are selected for registration or deletion, in `JvmtiAgentSet`. 2. Next, inspect `JvmtiResolver` for shared identity, native-lookup, and `Agent_OnUnload` registration. 3. Finally, review the OS library loading and startup entry-point invocations in `JvmtiAgentLifecycle` and the hook in `jni_GetEnv`, JVMTI environment disposal, and VM initialization. ## Testing - Tiers 1–6 - `vmTestbase_nsk_jvmti_quick` - `vmTestbase_nsk_jvmti` - `open/test/hotspot/jtreg/serviceability/jvmti` - Tests in `test/hotspot/jtreg/serviceability/jvmti/model` exercise the agent model and externally observable JVMTI behavior independently of JFR. - Tests in `test/hotspot/jtreg/serviceability/jvmti/jfr` verify JFR event emission over the new agent model. - Stress testing - Non-JVMTI enabled builds - Non-JFR enabled builds Broader validation results and the before-and-after profiling demonstration are documented in [JDK-8392584](https://bugs.openjdk.org/browse/JDK-8392584). --- - [x] I confirm that I make this contribution in accordance with the [OpenJDK Interim AI Policy](https://openjdk.org/legal/ai). ------------- Commit messages: - 8392584 Changes: https://git.openjdk.org/jdk/pull/33700/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=33700&range=00 Issue: https://bugs.openjdk.org/browse/JDK-8392584 Stats: 7014 lines in 61 files changed: 5786 ins; 1043 del; 185 mod Patch: https://git.openjdk.org/jdk/pull/33700.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/33700/head:pull/33700 PR: https://git.openjdk.org/jdk/pull/33700
