Greetings,

We are adding support to let JFR report on Agents.

#### Design

An Agent is a library that uses any instrumentation or profiling APIs. Most 
agents are started and initialized on the command line, but agents can also be 
loaded dynamically during runtime. Because command line agents initialize 
during the VM startup sequence, they add to the overall startup time latency in 
getting the VM ready. The events will report on the time the agent took to 
initialize.

A JavaAgent is an agent written in the Java programming language, using the 
APIs in the package 
[[ava.lang.instrument](https://docs.oracle.com/en/java/javase/19/docs/api/java.instrument/java/lang/instrument/package-summary.html)

A JavaAgent is sometimes called a JPLIS agent, where the acronym JPLIS stands 
for Java Programming Language Instrumentation Services.

To report on JavaAgents, JFR will add the new event type jdk.JavaAgent and 
events will look similar to these two examples:

// Command line
jdk.JavaAgent {
  startTime = 12:31:19.789 (2023-03-08)
  name = "JavaAgent.jar"
  options = "foo=bar"
  initialization = 12:31:15.574 (2023-03-08)
  initializationTime = 172 ms
  initializationMethod = "premain"
}
// Dynamic load
jdk.JavaAgent {
  startTime = 12:31:31.158 (2023-03-08)
  name = "JavaAgent.jar"
  options = "bar=baz"
  initialization = 12:31:31.037 (2023-03-08)
  initializationTime = 64,1 ms
  initializationMethod = "agentmain"
}

The jdk.JavaAgent event type is a JFR periodic event that iterates over running 
Java agents.

For a JavaAgent event, the agent's name will be the specific .jar file 
containing the instrumentation code. The options will be the specific options 
passed to the .jar file as part of launching the agent, for example, on the 
command line: -javaagent: JavaAgent.jar=foo=bar

The event will also detail which initialization method was invoked by the JVM, 
"premain" for command line agents, and "agentmain" for agents loaded 
dynamically.

"initialization" is the timestamp the JVM invoked the initialization method, 
and "initializationTime" is the duration of executing the initialization method.

"startTime" represents the time the JFR framework issued the periodic event; 
hence "initialization" will be earlier than "startTime".

An agent can also be written in a native programming language, using either the 
JVM Tools Interface (JVMTI) or JVM Profiling Interface (JVMPI). This kind of 
agent, sometimes called a native agent, is a platform-specific binary, 
sometimes referred to as a library, but here it means a .so or .dll file.

JVMTI standard spec:ification is 
[here](https://docs.oracle.com/en/java/javase/19/docs/specs/jvmti.html)

JVMPI is an older interface, not a standard and is considered superseded by 
JVMTI, but the support is still in the JVM for agents started on the command 
line:  -XRunMyAgent.jar

To report on native agents, JFR will add the new event type jdk.NativeAgent and 
events will look similar to this example:

jdk.NativeAgent {
  startTime = 12:31:40.398 (2023-03-08)
  name = "jdwp"
  options = "transport=dt_socket,server=y,address=any,onjcmd=y"
  path = 
"c:\ade\github\openjdk\jdk\build\windows-x86_64-server-slowdebug\jdk\bin\jdwp.dll"
  dynamic = false
  initialization = 12:31:36.142 (2023-03-08)
  initializationTime = 0,00184 ms
}

The layout of the event type is very similar to the jdk.JavaAgent event, but 
here the path to the native library is reported, and there is also a denotation 
if the agent was loaded via the command line (dynamic = false) or dynamically 
(dynamic = true).

The initialization of a native agent is performed by invoking an 
agent-specified callback routine which is not detailed in the event (for now). 
The "initialization" is the time the JVM sent or would have sent the JVMTI 
VMInit event to a specified callback. "initializationTime" is the duration to 
execute that specific callback. If no callback is specified for the JVMTI 
VMInit event, the "initializationTime" will be 0.

#### Implementation

There has not existed a reification of a JavaAgent directly in the JVM, as 
these are built on top of the JDK native library, "instrument", using a 
many-to-one mapping. At the level of the JVM, the only representation of agents 
after startup is through JvmtiEnv's, which agents request from the JVM during 
startup and initialization — as such, mapping which JvmtiEnv belongs to what 
JavaAgent was not possible before.

Using implementation details of how the JDK native library "instrument" 
interacts with the JVM, we can build this mapping and use it to track what 
JvmtiEnv's "belong" to what JavaAgent. This mapping now lets us report the 
Java-relevant context (name, options) and measure the time it takes for the 
JavaAgent to initialize.

In order to implement this capability, it was necessary to refactor the code 
used to represent agents, called AgentLibrary. The previous implementation was 
located primarily in arguments.cpp, and threads.cpp but also jvmtiExport.cpp.

The refactoring isolates the relevant logic into two new modules, 
prims/agent.hpp and prims/agentList.hpp. Breaking out this code from their 
older places will help reduce the sizes of oversized arguments.cpp and 
threads.cpp.

The previous lists used to maintain the agents (JVMTI) and libraries (JVMPI) is 
not thread-safe for concurrent iterations. A single list that allows for 
concurrent iterations is therefore introduced.

Testing: jdk_jfr, tier 1 - 6

Thanks
Markus

-------------

Commit messages:
 - event_names
 - adjustment
 - 8257967

Changes: https://git.openjdk.org/jdk/pull/12923/files
 Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=12923&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8257967
  Stats: 1862 lines in 22 files changed: 1353 ins; 485 del; 24 mod
  Patch: https://git.openjdk.org/jdk/pull/12923.diff
  Fetch: git fetch https://git.openjdk.org/jdk pull/12923/head:pull/12923

PR: https://git.openjdk.org/jdk/pull/12923

Reply via email to