Expand description
§jvmti
Complete JNI and JVMTI bindings for Rust with zero dependencies.
This crate provides everything you need to build JVM agents in Rust:
- Low-level FFI bindings to JNI and JVMTI
- High-level wrappers with ergonomic Rust APIs
- The
Agenttrait andexport_agent!macro for easy agent creation
§Features
- Complete Coverage: All 236 JNI functions, all 156 JVMTI functions
- Zero Dependencies: No external crates required
- Ergonomic API: High-level wrappers handle strings, arrays, references
- Type-Safe: Proper Rust types,
Resultreturns, RAII guards - JDK 8-27 Compatible: Verified against JDK 27 headers
§Quick Start
Create a minimal agent in 4 steps:
1. Create a new library crate:
cargo new --lib my_agent2. Configure Cargo.toml:
[lib]
crate-type = ["cdylib"]
[dependencies]
jvmti = "0.1"3. Implement your agent (src/lib.rs):
ⓘ
use jvmti_bindings::prelude::*;
#[derive(Default)]
struct MyAgent;
impl Agent for MyAgent {
fn on_load(&self, vm: *mut jni::JavaVM, options: &str) -> jni::jint {
println!("[MyAgent] Loaded with options: {}", options);
jni::JNI_OK
}
fn vm_init(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread) {
println!("[MyAgent] VM initialized!");
}
fn vm_death(&self, _jni: *mut jni::JNIEnv) {
println!("[MyAgent] VM shutting down");
}
}
export_agent!(MyAgent);4. Build and run:
cargo build --release
java -agentpath:./target/release/libmy_agent.so=myoptions MyApp§Architecture
The crate is organized in layers:
┌─────────────────────────────────────────────────────────┐
│ Your Agent Code │
│ impl Agent for MyAgent { ... } │
├─────────────────────────────────────────────────────────┤
│ Agent Trait + Macros │
│ Agent, export_agent!, get_default_callbacks() │
├─────────────────────────────────────────────────────────┤
│ High-Level Wrappers (env module) │
│ env::Jvmti - JVMTI operations with Result returns │
│ env::JniEnv - JNI operations with string helpers │
│ env::LocalRef, GlobalRef - RAII reference guards │
├─────────────────────────────────────────────────────────┤
│ Raw FFI Bindings (sys module) │
│ sys::jni - JNI types, vtable (236 functions) │
│ sys::jvmti - JVMTI types, vtable (156 functions) │
└─────────────────────────────────────────────────────────┘§Modules
| Module | Purpose |
|---|---|
sys::jni | Raw JNI types and vtable (for FFI) |
sys::jvmti | Raw JVMTI types, vtable, capabilities, events |
[env] | High-level wrappers - start here for ergonomic APIs |
env::Jvmti | JVMTI environment wrapper (153 methods) |
env::JniEnv | JNI environment wrapper (60+ methods) |
classfile | Class file parser with all Java 8-27 attributes |
prelude | Recommended imports for agents |
advanced | Feature-gated advanced helpers (heap graph utilities) |
§Enabling JVMTI Events
To receive JVMTI events, you must:
- Request the required capabilities
- Set up event callbacks
- Enable the specific events
ⓘ
use jvmti_bindings::prelude::*;
#[derive(Default)]
struct ClassMonitor;
impl Agent for ClassMonitor {
fn on_load(&self, vm: *mut jni::JavaVM, _options: &str) -> jni::jint {
let jvmti_env = Jvmti::new(vm).expect("Failed to get JVMTI");
// 1. Request capabilities
let mut caps = jvmti::jvmtiCapabilities::default();
caps.set_can_generate_all_class_hook_events(true);
jvmti_env.add_capabilities(&caps).expect("capabilities");
// 2. Set up callbacks (wires all events to your Agent impl)
let callbacks = get_default_callbacks();
jvmti_env.set_event_callbacks(callbacks).expect("callbacks");
// 3. Enable specific events
jvmti_env.set_event_notification_mode(
true, // enable
jvmti::JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
std::ptr::null_mut() // all threads
).expect("enable event");
jni::JNI_OK
}
fn class_file_load_hook(&self, _jni: *mut jni::JNIEnv, /* ... */) {
// Called for every class load!
}
}
export_agent!(ClassMonitor);§Working with JNI
Use env::JniEnv for ergonomic JNI operations:
ⓘ
use jvmti_bindings::prelude::*;
fn vm_init(&self, jni_ptr: *mut jni::JNIEnv, _thread: jni::jthread) {
let jni = unsafe { JniEnv::from_raw(jni_ptr) };
// Find a class
let system_class = jni.find_class("java/lang/System").unwrap();
// Get a static field
let out_field = jni.get_static_field_id(system_class, "out", "Ljava/io/PrintStream;").unwrap();
let out = jni.get_static_object_field(system_class, out_field);
// Create a Java string
let message = jni.new_string_utf("Hello from Rust!").unwrap();
// Call a method
let print_class = jni.find_class("java/io/PrintStream").unwrap();
let println_method = jni.get_method_id(print_class, "println", "(Ljava/lang/String;)V").unwrap();
jni.call_void_method(out, println_method, &[jni::jvalue { l: message }]);
// Check for exceptions
if jni.exception_check() {
jni.exception_describe();
jni.exception_clear();
}
}§Version Compatibility
| JDK Version | JNI Functions | JVMTI Functions | Notes |
|---|---|---|---|
| 8 | 232 | 153 | Baseline |
| 9 | 233 | 156 | +GetModule, +Module functions |
| 11 | 233 | 156 | +SetHeapSamplingInterval |
| 21 | 234 | 156 | +IsVirtualThread, +Virtual thread support |
| 24/25 | 235 | 156 | +GetStringUTFLengthAsLong |
| 27 | 235 | 156 | +ClearAllFramePops (slot 67) |
Re-exports§
pub use crate::sys::jni;
Modules§
- advanced
- Advanced helpers for JVMTI power users.
- classfile
- Class file parser for Java 8 through 27.
- embed
- Helpers for embedding a JVM inside a Rust process.
- env
- High-level environment wrappers for JVMTI and JNI.
- prelude
- Common imports for building JVMTI agents.
- sys
Macros§
- export_
agent - Exports your agent type as a loadable JVMTI agent library.
- jni_
call - Helper to call JNI functions through the vtable. env_ptr: *mut JNIEnv = *mut *const JNINativeInterface_ *env_ptr: *const JNINativeInterface_ (vtable pointer) **env_ptr: JNINativeInterface_ (vtable itself) Usage: jni_call!(env, FindClass, b“java/lang/String\0“.as_ptr() as *const c_char)
- jvm_
call - Helper to call JavaVM functions through the vtable. vm_ptr: *mut JavaVM = *mut *const JNIInvokeInterface_ *vm_ptr: *const JNIInvokeInterface_ (vtable pointer) **vm_ptr: JNIInvokeInterface_ (vtable itself)
Statics§
Traits§
- Agent
- The core trait for implementing a JVMTI agent.
Functions§
- get_
default_ callbacks - Returns a pre-configured
jvmtiEventCallbacksstruct with all event trampolines wired up. - set_
global_ agent - Helper to initialize the global agent (called by the macro)