Skip to main content

jvmti_bindings/
lib.rs

1//! # jvmti
2//!
3//! Complete JNI and JVMTI bindings for Rust with **zero dependencies**.
4//!
5//! This crate provides everything you need to build JVM agents in Rust:
6//! - Low-level FFI bindings to JNI and JVMTI
7//! - High-level wrappers with ergonomic Rust APIs
8//! - The [`Agent`] trait and [`export_agent!`] macro for easy agent creation
9//!
10//! ## Features
11//!
12//! - **Complete Coverage**: All 236 JNI functions, all 156 JVMTI functions
13//! - **Zero Dependencies**: No external crates required
14//! - **Ergonomic API**: High-level wrappers handle strings, arrays, references
15//! - **Type-Safe**: Proper Rust types, `Result` returns, RAII guards
16//! - **JDK 8-27 Compatible**: Verified against JDK 27 headers
17//!
18//! ## Quick Start
19//!
20//! Create a minimal agent in 4 steps:
21//!
22//! **1. Create a new library crate:**
23//! ```bash
24//! cargo new --lib my_agent
25//! ```
26//!
27//! **2. Configure Cargo.toml:**
28//! ```toml
29//! [lib]
30//! crate-type = ["cdylib"]
31//!
32//! [dependencies]
33//! jvmti = "0.1"
34//! ```
35//!
36//! **3. Implement your agent (src/lib.rs):**
37//! ```rust,ignore
38//! use jvmti_bindings::prelude::*;
39//!
40//! #[derive(Default)]
41//! struct MyAgent;
42//!
43//! impl Agent for MyAgent {
44//!     fn on_load(&self, vm: *mut jni::JavaVM, options: &str) -> jni::jint {
45//!         println!("[MyAgent] Loaded with options: {}", options);
46//!         jni::JNI_OK
47//!     }
48//!
49//!     fn vm_init(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread) {
50//!         println!("[MyAgent] VM initialized!");
51//!     }
52//!
53//!     fn vm_death(&self, _jni: *mut jni::JNIEnv) {
54//!         println!("[MyAgent] VM shutting down");
55//!     }
56//! }
57//!
58//! export_agent!(MyAgent);
59//! ```
60//!
61//! **4. Build and run:**
62//! ```bash
63//! cargo build --release
64//! java -agentpath:./target/release/libmy_agent.so=myoptions MyApp
65//! ```
66//!
67//! ## Architecture
68//!
69//! The crate is organized in layers:
70//!
71//! ```text
72//! ┌─────────────────────────────────────────────────────────┐
73//! │                    Your Agent Code                       │
74//! │         impl Agent for MyAgent { ... }                   │
75//! ├─────────────────────────────────────────────────────────┤
76//! │                   Agent Trait + Macros                   │
77//! │      Agent, export_agent!, get_default_callbacks()       │
78//! ├─────────────────────────────────────────────────────────┤
79//! │              High-Level Wrappers (env module)            │
80//! │   env::Jvmti - JVMTI operations with Result returns      │
81//! │   env::JniEnv - JNI operations with string helpers       │
82//! │   env::LocalRef, GlobalRef - RAII reference guards       │
83//! ├─────────────────────────────────────────────────────────┤
84//! │              Raw FFI Bindings (sys module)               │
85//! │   sys::jni - JNI types, vtable (236 functions)           │
86//! │   sys::jvmti - JVMTI types, vtable (156 functions)       │
87//! └─────────────────────────────────────────────────────────┘
88//! ```
89//!
90//! ## Modules
91//!
92//! | Module | Purpose |
93//! |--------|---------|
94//! | [`sys::jni`] | Raw JNI types and vtable (for FFI) |
95//! | [`sys::jvmti`] | Raw JVMTI types, vtable, capabilities, events |
96//! | [`env`] | **High-level wrappers** - start here for ergonomic APIs |
97//! | [`env::Jvmti`] | JVMTI environment wrapper (153 methods) |
98//! | [`env::JniEnv`] | JNI environment wrapper (60+ methods) |
99//! | [`classfile`] | Class file parser with all Java 8-27 attributes |
100//! | [`prelude`] | Recommended imports for agents |
101//! | [`advanced`] | Feature-gated advanced helpers (heap graph utilities) |
102//!
103//! ## Enabling JVMTI Events
104//!
105//! To receive JVMTI events, you must:
106//! 1. Request the required capabilities
107//! 2. Set up event callbacks
108//! 3. Enable the specific events
109//!
110//! ```rust,ignore
111//! use jvmti_bindings::prelude::*;
112//!
113//! #[derive(Default)]
114//! struct ClassMonitor;
115//!
116//! impl Agent for ClassMonitor {
117//!     fn on_load(&self, vm: *mut jni::JavaVM, _options: &str) -> jni::jint {
118//!         let jvmti_env = Jvmti::new(vm).expect("Failed to get JVMTI");
119//!
120//!         // 1. Request capabilities
121//!         let mut caps = jvmti::jvmtiCapabilities::default();
122//!         caps.set_can_generate_all_class_hook_events(true);
123//!         jvmti_env.add_capabilities(&caps).expect("capabilities");
124//!
125//!         // 2. Set up callbacks (wires all events to your Agent impl)
126//!         let callbacks = get_default_callbacks();
127//!         jvmti_env.set_event_callbacks(callbacks).expect("callbacks");
128//!
129//!         // 3. Enable specific events
130//!         jvmti_env.set_event_notification_mode(
131//!             true,  // enable
132//!             jvmti::JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
133//!             std::ptr::null_mut()  // all threads
134//!         ).expect("enable event");
135//!
136//!         jni::JNI_OK
137//!     }
138//!
139//!     fn class_file_load_hook(&self, _jni: *mut jni::JNIEnv, /* ... */) {
140//!         // Called for every class load!
141//!     }
142//! }
143//!
144//! export_agent!(ClassMonitor);
145//! ```
146//!
147//! ## Working with JNI
148//!
149//! Use [`env::JniEnv`] for ergonomic JNI operations:
150//!
151//! ```rust,ignore
152//! use jvmti_bindings::prelude::*;
153//!
154//! fn vm_init(&self, jni_ptr: *mut jni::JNIEnv, _thread: jni::jthread) {
155//!     let jni = unsafe { JniEnv::from_raw(jni_ptr) };
156//!
157//!     // Find a class
158//!     let system_class = jni.find_class("java/lang/System").unwrap();
159//!
160//!     // Get a static field
161//!     let out_field = jni.get_static_field_id(system_class, "out", "Ljava/io/PrintStream;").unwrap();
162//!     let out = jni.get_static_object_field(system_class, out_field);
163//!
164//!     // Create a Java string
165//!     let message = jni.new_string_utf("Hello from Rust!").unwrap();
166//!
167//!     // Call a method
168//!     let print_class = jni.find_class("java/io/PrintStream").unwrap();
169//!     let println_method = jni.get_method_id(print_class, "println", "(Ljava/lang/String;)V").unwrap();
170//!     jni.call_void_method(out, println_method, &[jni::jvalue { l: message }]);
171//!
172//!     // Check for exceptions
173//!     if jni.exception_check() {
174//!         jni.exception_describe();
175//!         jni.exception_clear();
176//!     }
177//! }
178//! ```
179//!
180//! ## Version Compatibility
181//!
182//! | JDK Version | JNI Functions | JVMTI Functions | Notes |
183//! |-------------|---------------|-----------------|-------|
184//! | 8           | 232           | 153             | Baseline |
185//! | 9           | 233           | 156             | +GetModule, +Module functions |
186//! | 11          | 233           | 156             | +SetHeapSamplingInterval |
187//! | 21          | 234           | 156             | +IsVirtualThread, +Virtual thread support |
188//! | 24/25       | 235           | 156             | +GetStringUTFLengthAsLong |
189//! | 27          | 235           | 156             | +ClearAllFramePops (slot 67) |
190
191pub mod sys;
192pub mod env;
193pub mod classfile;
194pub mod prelude;
195#[cfg(feature = "embed")]
196pub mod embed;
197#[cfg(feature = "advanced")]
198pub mod advanced;
199
200// Implementation modules (use `env` module for the public API)
201#[doc(hidden)]
202pub(crate) mod jvmti_wrapper;
203#[doc(hidden)]
204pub(crate) mod jni_wrapper;
205
206use std::sync::OnceLock;
207pub use crate::sys::jni as jni;
208use crate::sys::jvmti as jvmti;
209
210/// The core trait for implementing a JVMTI agent.
211///
212/// Implement this trait and use [`export_agent!`] to create a loadable agent library.
213/// All event methods have default no-op implementations, so you only need to override
214/// the ones you care about.
215///
216/// # Thread Safety
217///
218/// Your agent must be `Sync + Send` because JVMTI events can fire from any thread.
219/// Use appropriate synchronization (e.g., `Mutex`, `RwLock`, atomics) for shared state.
220///
221/// # Example
222///
223/// ```rust,ignore
224/// use jvmti_bindings::prelude::*;
225///
226/// #[derive(Default)]
227/// struct MyProfiler {
228///     method_count: std::sync::atomic::AtomicU64,
229/// }
230///
231/// impl Agent for MyProfiler {
232///     fn on_load(&self, vm: *mut jni::JavaVM, options: &str) -> jni::jint {
233///         println!("Profiler loaded!");
234///         jni::JNI_OK
235///     }
236///
237///     fn method_entry(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID) {
238///         self.method_count.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
239///     }
240/// }
241///
242/// export_agent!(MyProfiler);
243/// ```
244///
245/// # Capabilities
246///
247/// Many events require specific JVMTI capabilities to be enabled. Use
248/// [`env::Jvmti::add_capabilities`] in your `on_load` to request them.
249pub trait Agent: Sync + Send {
250    /// Called when the agent is loaded into the JVM.
251    ///
252    /// This is your initialization point. Use it to:
253    /// - Parse agent options
254    /// - Request JVMTI capabilities
255    /// - Set up event callbacks
256    /// - Initialize your agent's state
257    ///
258    /// Return `JNI_OK` (0) on success, or `JNI_ERR` (-1) on failure.
259    fn on_load(&self, vm: *mut jni::JavaVM, options: &str) -> jni::jint;
260
261    /// Called when the agent is attached to a running JVM (dynamic attach).
262    ///
263    /// Default implementation returns `JNI_OK`.
264    fn on_attach(&self, _vm: *mut jni::JavaVM, _options: &str) -> jni::jint {
265        jni::JNI_OK
266    }
267
268    /// Called when the agent is unloaded (JVM shutdown).
269    ///
270    /// Use this for cleanup: flush buffers, close files, etc.
271    fn on_unload(&self) {}
272
273    // =========================================================================
274    // VM LIFECYCLE EVENTS
275    // =========================================================================
276
277    /// Called when the VM initialization is complete.
278    ///
279    /// At this point, JNI is fully functional and you can safely call JNI functions.
280    /// The `thread` parameter is the main thread.
281    fn vm_init(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread) {}
282
283    /// Called when the VM is about to terminate.
284    ///
285    /// This is your last chance to perform cleanup that requires JNI.
286    fn vm_death(&self, _jni: *mut jni::JNIEnv) {}
287
288    /// Called when the VM starts (before `vm_init`).
289    ///
290    /// JNI is available but limited - you cannot create new threads or load classes.
291    /// Requires `can_generate_early_vmstart` capability for early delivery.
292    fn vm_start(&self, _jni: *mut jni::JNIEnv) {}
293
294    // =========================================================================
295    // THREAD EVENTS
296    // =========================================================================
297
298    /// Called when a new thread starts.
299    ///
300    /// Fired for every thread including the main thread.
301    fn thread_start(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread) {}
302
303    /// Called when a thread is about to terminate.
304    fn thread_end(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread) {}
305
306    // =========================================================================
307    // CLASS EVENTS
308    // =========================================================================
309
310    /// Called when a class is first loaded (before linking).
311    ///
312    /// The class is not yet usable at this point.
313    fn class_load(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _klass: jni::jclass) {}
314
315    /// Called when a class is prepared (linked and ready to use).
316    ///
317    /// At this point you can query the class's methods and fields.
318    fn class_prepare(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _klass: jni::jclass) {}
319
320    /// Called when class bytecode is being loaded or redefined.
321    ///
322    /// This is your hook for bytecode instrumentation (BCI). To modify the class:
323    /// 1. Allocate memory with `Jvmti::allocate()`
324    /// 2. Write your modified bytecode to it
325    /// 3. Set `new_class_data_len` and `new_class_data`
326    ///
327    /// Requires `can_generate_all_class_hook_events` or `can_retransform_classes`.
328    fn class_file_load_hook(&self, _jni: *mut jni::JNIEnv, _class_being_redefined: jni::jclass,
329                            _loader: jni::jobject, _name: *const std::os::raw::c_char,
330                            _protection_domain: jni::jobject, _class_data_len: jni::jint,
331                            _class_data: *const std::os::raw::c_uchar,
332                            _new_class_data_len: *mut jni::jint,
333                            _new_class_data: *mut *mut std::os::raw::c_uchar) {}
334
335    // =========================================================================
336    // METHOD EVENTS
337    // =========================================================================
338
339    /// Called when a method is entered.
340    ///
341    /// **Warning**: This fires for EVERY method call - extremely high overhead.
342    /// Requires `can_generate_method_entry_events` capability.
343    fn method_entry(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID) {}
344
345    /// Called when a method is about to return.
346    ///
347    /// **Warning**: This fires for EVERY method return - extremely high overhead.
348    /// Requires `can_generate_method_exit_events` capability.
349    fn method_exit(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID) {}
350
351    /// Called when a native method is bound to its implementation.
352    ///
353    /// You can redirect native methods by setting `*new_address_ptr`.
354    /// Requires `can_generate_native_method_bind_events` capability.
355    fn native_method_bind(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID, _address: *mut std::os::raw::c_void, _new_address_ptr: *mut *mut std::os::raw::c_void) {}
356
357    // =========================================================================
358    // COMPILED CODE EVENTS (JIT)
359    // =========================================================================
360
361    /// Called when a method is JIT-compiled.
362    ///
363    /// Useful for profilers that need to map native code addresses to methods.
364    /// Requires `can_generate_compiled_method_load_events` capability.
365    fn compiled_method_load(&self, _method: jni::jmethodID, _code_size: jni::jint, _code_addr: *const std::os::raw::c_void, _map_length: jni::jint, _map: *const std::os::raw::c_void, _compile_info: *const std::os::raw::c_void) {}
366
367    /// Called when JIT-compiled code is unloaded (deoptimized).
368    fn compiled_method_unload(&self, _method: jni::jmethodID, _code_addr: *const std::os::raw::c_void) {}
369
370    /// Called when dynamic code is generated (e.g., JIT stubs).
371    fn dynamic_code_generated(&self, _name: *const std::os::raw::c_char, _address: *const std::os::raw::c_void, _length: jni::jint) {}
372
373    // =========================================================================
374    // EXCEPTION EVENTS
375    // =========================================================================
376
377    /// Called when an exception is thrown.
378    ///
379    /// `catch_method` and `catch_location` indicate where it will be caught,
380    /// or are null/0 if uncaught. Requires `can_generate_exception_events`.
381    fn exception(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID,
382                 _location: jvmti::jlocation, _exception: jni::jobject,
383                 _catch_method: jni::jmethodID, _catch_location: jvmti::jlocation) {}
384
385    /// Called when an exception is caught.
386    fn exception_catch(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID,
387                       _location: jvmti::jlocation, _exception: jni::jobject) {}
388
389    // =========================================================================
390    // DEBUGGING EVENTS
391    // =========================================================================
392
393    /// Called before each bytecode instruction (single-stepping).
394    ///
395    /// **Extreme overhead** - only use for debugging.
396    /// Requires `can_generate_single_step_events` capability.
397    fn single_step(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID, _location: jvmti::jlocation) {}
398
399    /// Called when a breakpoint is hit.
400    ///
401    /// Requires `can_generate_breakpoint_events` capability.
402    fn breakpoint(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID, _location: jvmti::jlocation) {}
403
404    /// Called when a frame is popped (method returns or exception thrown).
405    ///
406    /// Must be registered per-frame with `notify_frame_pop`.
407    /// Requires `can_generate_frame_pop_events` capability.
408    fn frame_pop(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID, _was_popped_by_exception: jni::jboolean) {}
409
410    // =========================================================================
411    // MONITOR EVENTS
412    // =========================================================================
413
414    /// Called when a thread is about to wait on a monitor (`Object.wait()`).
415    ///
416    /// Requires `can_generate_monitor_events` capability.
417    fn monitor_wait(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _object: jni::jobject, _timeout: jni::jlong) {}
418
419    /// Called when a thread finishes waiting on a monitor.
420    ///
421    /// `timed_out` indicates if the wait timed out.
422    fn monitor_waited(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _object: jni::jobject, _timed_out: jni::jboolean) {}
423
424    /// Called when a thread is about to block on a contended monitor.
425    fn monitor_contended_enter(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _object: jni::jobject) {}
426
427    /// Called when a thread acquires a previously contended monitor.
428    fn monitor_contended_entered(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _object: jni::jobject) {}
429
430    // =========================================================================
431    // FIELD EVENTS (WATCHPOINTS)
432    // =========================================================================
433
434    /// Called when a watched field is read.
435    ///
436    /// Set up with `set_field_access_watch`. Requires `can_generate_field_access_events`.
437    fn field_access(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID,
438                    _location: jvmti::jlocation, _field_klass: jni::jclass, _object: jni::jobject, _field: jni::jfieldID) {}
439
440    /// Called when a watched field is modified.
441    ///
442    /// Set up with `set_field_modification_watch`. Requires `can_generate_field_modification_events`.
443    fn field_modification(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _method: jni::jmethodID,
444                          _location: jvmti::jlocation, _field_klass: jni::jclass, _object: jni::jobject,
445                          _field: jni::jfieldID, _sig_type: std::os::raw::c_char, _new_value: jni::jvalue) {}
446
447    // =========================================================================
448    // GC & MEMORY EVENTS
449    // =========================================================================
450
451    /// Called when garbage collection starts.
452    ///
453    /// **No JNI calls allowed** during this callback.
454    /// Requires `can_generate_garbage_collection_events` capability.
455    fn garbage_collection_start(&self) {}
456
457    /// Called when garbage collection finishes.
458    ///
459    /// **No JNI calls allowed** during this callback.
460    fn garbage_collection_finish(&self) {}
461
462    /// Called when a critical resource is exhausted (heap, threads, etc.).
463    fn resource_exhausted(&self, _jni: *mut jni::JNIEnv, _flags: jni::jint, _description: *const std::os::raw::c_char) {}
464
465    // =========================================================================
466    // OBJECT EVENTS
467    // =========================================================================
468
469    /// Called when a tagged object is garbage collected.
470    ///
471    /// Use `set_tag` to tag objects you want to track.
472    /// Requires `can_generate_object_free_events` capability.
473    fn object_free(&self, _tag: jni::jlong) {}
474
475    /// Called when an object is allocated (VM-internal allocations).
476    ///
477    /// Does NOT fire for all allocations - use sampling for comprehensive coverage.
478    /// Requires `can_generate_vm_object_alloc_events` capability.
479    fn vm_object_alloc(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _object: jni::jobject, _klass: jni::jclass, _size: jni::jlong) {}
480
481    /// Called for sampled object allocations (JDK 11+).
482    ///
483    /// Configure sampling rate with `set_heap_sampling_interval`.
484    /// Requires `can_generate_sampled_object_alloc_events` capability.
485    fn sampled_object_alloc(&self, _jni: *mut jni::JNIEnv, _thread: jni::jthread, _object: jni::jobject, _klass: jni::jclass, _size: jni::jlong) {}
486}
487
488// 2. THE GLOBAL SINGLETON
489// This holds the user's Agent instance so static C functions can find it.
490pub static GLOBAL_AGENT: OnceLock<Box<dyn Agent>> = OnceLock::new();
491
492/// Helper to initialize the global agent (called by the macro)
493pub fn set_global_agent(agent: Box<dyn Agent>) -> Result<(), ()> {
494    GLOBAL_AGENT.set(agent).map_err(|_| ())
495}
496
497unsafe extern "system" fn trampoline_method_entry(
498    _jvmti_env: *mut sys::jvmti::jvmtiEnv,
499    jni_env: *mut jni::JNIEnv,
500    thread: jni::jthread,
501    method: jni::jmethodID,
502) {
503    if let Some(agent) = GLOBAL_AGENT.get() {
504        agent.method_entry(jni_env, thread, method);
505    }
506}
507
508unsafe extern "system" fn trampoline_method_exit(
509    _jvmti_env: *mut sys::jvmti::jvmtiEnv,
510    jni_env: *mut jni::JNIEnv,
511    thread: jni::jthread,
512    method: jni::jmethodID,
513    _was_popped: jni::jboolean,
514    _ret_val: jni::jvalue,
515) {
516    if let Some(agent) = GLOBAL_AGENT.get() {
517        agent.method_exit(jni_env, thread, method);
518    }
519}
520
521unsafe extern "system" fn trampoline_native_method_bind(
522    _env: *mut sys::jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, method: jni::jmethodID,
523    address: *mut std::os::raw::c_void, new_address_ptr: *mut *mut std::os::raw::c_void
524) {
525    if let Some(agent) = GLOBAL_AGENT.get() { agent.native_method_bind(jni, thread, method, address, new_address_ptr); }
526}
527
528
529// --- 1. Lifecycle ---
530unsafe extern "system" fn trampoline_vm_init(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread) {
531    if let Some(agent) = GLOBAL_AGENT.get() { agent.vm_init(jni, thread); }
532}
533unsafe extern "system" fn trampoline_vm_death(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv) {
534    if let Some(agent) = GLOBAL_AGENT.get() { agent.vm_death(jni); }
535}
536unsafe extern "system" fn trampoline_vm_start(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv) {
537    if let Some(agent) = GLOBAL_AGENT.get() { agent.vm_start(jni); }
538}
539
540// --- 2. Threads ---
541unsafe extern "system" fn trampoline_thread_start(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread) {
542    if let Some(agent) = GLOBAL_AGENT.get() { agent.thread_start(jni, thread); }
543}
544unsafe extern "system" fn trampoline_thread_end(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread) {
545    if let Some(agent) = GLOBAL_AGENT.get() { agent.thread_end(jni, thread); }
546}
547
548// --- 3. Classes ---
549unsafe extern "system" fn trampoline_class_load(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, klass: jni::jclass) {
550    if let Some(agent) = GLOBAL_AGENT.get() { agent.class_load(jni, thread, klass); }
551}
552unsafe extern "system" fn trampoline_class_prepare(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, klass: jni::jclass) {
553    if let Some(agent) = GLOBAL_AGENT.get() { agent.class_prepare(jni, thread, klass); }
554}
555
556// --- 3.5 Compiled Code ---
557unsafe extern "system" fn trampoline_compiled_method_load(
558    _env: *mut jvmti::jvmtiEnv, method: jni::jmethodID, code_size: jni::jint, code_addr: *const std::os::raw::c_void,
559    map_length: jni::jint, map: *const std::os::raw::c_void, compile_info: *const std::os::raw::c_void
560) {
561    if let Some(agent) = GLOBAL_AGENT.get() { agent.compiled_method_load(method, code_size, code_addr, map_length, map, compile_info); }
562}
563unsafe extern "system" fn trampoline_compiled_method_unload(_env: *mut jvmti::jvmtiEnv, method: jni::jmethodID, code_addr: *const std::os::raw::c_void) {
564    if let Some(agent) = GLOBAL_AGENT.get() { agent.compiled_method_unload(method, code_addr); }
565}
566unsafe extern "system" fn trampoline_dynamic_code_generated(_env: *mut jvmti::jvmtiEnv, name: *const std::os::raw::c_char, address: *const std::os::raw::c_void, length: jni::jint) {
567    if let Some(agent) = GLOBAL_AGENT.get() { agent.dynamic_code_generated(name, address, length); }
568}
569unsafe extern "system" fn trampoline_class_file_load_hook(
570    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv,
571    class_being_redefined: jni::jclass, loader: jni::jobject, name: *const std::os::raw::c_char,
572    protection_domain: jni::jobject, class_data_len: jni::jint, class_data: *const std::os::raw::c_uchar,
573    new_class_data_len: *mut jni::jint, new_class_data: *mut *mut std::os::raw::c_uchar
574) {
575    if let Some(agent) = GLOBAL_AGENT.get() {
576        agent.class_file_load_hook(jni, class_being_redefined, loader, name, protection_domain, class_data_len, class_data, new_class_data_len, new_class_data);
577    }
578}
579
580// --- 4. Exceptions ---
581unsafe extern "system" fn trampoline_exception(
582    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, method: jni::jmethodID,
583    location: jvmti::jlocation, exception: jni::jobject, catch_method: jni::jmethodID, catch_location: jvmti::jlocation
584) {
585    if let Some(agent) = GLOBAL_AGENT.get() {
586        agent.exception(jni, thread, method, location, exception, catch_method, catch_location);
587    }
588}
589unsafe extern "system" fn trampoline_exception_catch(
590    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, method: jni::jmethodID,
591    location: jvmti::jlocation, exception: jni::jobject
592) {
593    if let Some(agent) = GLOBAL_AGENT.get() {
594        agent.exception_catch(jni, thread, method, location, exception);
595    }
596}
597
598// --- 5. Debugging ---
599unsafe extern "system" fn trampoline_single_step(
600    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, method: jni::jmethodID, location: jvmti::jlocation
601) {
602    if let Some(agent) = GLOBAL_AGENT.get() { agent.single_step(jni, thread, method, location); }
603}
604unsafe extern "system" fn trampoline_breakpoint(
605    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, method: jni::jmethodID, location: jvmti::jlocation
606) {
607    if let Some(agent) = GLOBAL_AGENT.get() { agent.breakpoint(jni, thread, method, location); }
608}
609unsafe extern "system" fn trampoline_frame_pop(
610    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, method: jni::jmethodID, was_popped: jni::jboolean
611) {
612    if let Some(agent) = GLOBAL_AGENT.get() { agent.frame_pop(jni, thread, method, was_popped); }
613}
614
615// --- 5.5 Monitors ---
616unsafe extern "system" fn trampoline_monitor_wait(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, object: jni::jobject, timeout: jni::jlong) {
617    if let Some(agent) = GLOBAL_AGENT.get() { agent.monitor_wait(jni, thread, object, timeout); }
618}
619unsafe extern "system" fn trampoline_monitor_waited(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, object: jni::jobject, timed_out: jni::jboolean) {
620    if let Some(agent) = GLOBAL_AGENT.get() { agent.monitor_waited(jni, thread, object, timed_out); }
621}
622unsafe extern "system" fn trampoline_monitor_contended_enter(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, object: jni::jobject) {
623    if let Some(agent) = GLOBAL_AGENT.get() { agent.monitor_contended_enter(jni, thread, object); }
624}
625unsafe extern "system" fn trampoline_monitor_contended_entered(_env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, object: jni::jobject) {
626    if let Some(agent) = GLOBAL_AGENT.get() { agent.monitor_contended_entered(jni, thread, object); }
627}
628
629// --- 6. Fields ---
630unsafe extern "system" fn trampoline_field_access(
631    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, method: jni::jmethodID,
632    location: jvmti::jlocation, field_klass: jni::jclass, object: jni::jobject, field: crate::sys::jni::jfieldID
633) {
634    if let Some(agent) = GLOBAL_AGENT.get() { agent.field_access(jni, thread, method, location, field_klass, object, field); }
635}
636unsafe extern "system" fn trampoline_field_modification(
637    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread, method: jni::jmethodID,
638    location: jvmti::jlocation, field_klass: jni::jclass, object: jni::jobject, field: crate::sys::jni::jfieldID,
639    sig_type: std::os::raw::c_char, new_value: jni::jvalue
640) {
641    if let Some(agent) = GLOBAL_AGENT.get() { agent.field_modification(jni, thread, method, location, field_klass, object, field, sig_type, new_value); }
642}
643
644// --- 7. GC & Resource ---
645unsafe extern "system" fn trampoline_garbage_collection_start(_env: *mut jvmti::jvmtiEnv) {
646    if let Some(agent) = GLOBAL_AGENT.get() { agent.garbage_collection_start(); }
647}
648unsafe extern "system" fn trampoline_garbage_collection_finish(_env: *mut jvmti::jvmtiEnv) {
649    if let Some(agent) = GLOBAL_AGENT.get() { agent.garbage_collection_finish(); }
650}
651unsafe extern "system" fn trampoline_resource_exhausted(
652    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, flags: jni::jint,
653    _reserved: *const std::os::raw::c_void, description: *const std::os::raw::c_char
654) {
655    if let Some(agent) = GLOBAL_AGENT.get() { agent.resource_exhausted(jni, flags, description); }
656}
657
658// --- 8. Objects ---
659unsafe extern "system" fn trampoline_object_free(_env: *mut jvmti::jvmtiEnv, tag: jni::jlong) {
660    if let Some(agent) = GLOBAL_AGENT.get() { agent.object_free(tag); }
661}
662unsafe extern "system" fn trampoline_vm_object_alloc(
663    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread,
664    object: jni::jobject, klass: jni::jclass, size: jni::jlong
665) {
666    if let Some(agent) = GLOBAL_AGENT.get() { agent.vm_object_alloc(jni, thread, object, klass, size); }
667}
668unsafe extern "system" fn trampoline_sampled_object_alloc(
669    _env: *mut jvmti::jvmtiEnv, jni: *mut jni::JNIEnv, thread: jni::jthread,
670    object: jni::jobject, klass: jni::jclass, size: jni::jlong
671) {
672    if let Some(agent) = GLOBAL_AGENT.get() { agent.sampled_object_alloc(jni, thread, object, klass, size); }
673}
674
675
676
677
678/// Returns a pre-configured `jvmtiEventCallbacks` struct with all event trampolines wired up.
679///
680/// This function populates a callbacks struct that routes all JVMTI events to your
681/// [`Agent`] implementation via the global agent instance. Use this with
682/// [`env::Jvmti::set_event_callbacks`] to enable event delivery.
683///
684/// # Example
685///
686/// ```rust,ignore
687/// fn on_load(&self, vm: *mut jni::JavaVM, options: &str) -> jni::jint {
688///     let jvmti = Jvmti::new(vm).unwrap();
689///
690///     // Wire up all event callbacks
691///     let callbacks = get_default_callbacks();
692///     jvmti.set_event_callbacks(&callbacks);
693///
694///     // Enable specific events you care about
695///     jvmti.enable_event(
696///         jvmti::JVMTI_EVENT_VM_INIT,
697///         std::ptr::null_mut()
698///     );
699///
700///     jni::JNI_OK
701/// }
702/// ```
703///
704/// # Events Wired
705///
706/// All standard JVMTI events are wired:
707/// - VM lifecycle: `VMInit`, `VMDeath`, `VMStart`
708/// - Threads: `ThreadStart`, `ThreadEnd`
709/// - Classes: `ClassLoad`, `ClassPrepare`, `ClassFileLoadHook`
710/// - Methods: `MethodEntry`, `MethodExit`, `NativeMethodBind`
711/// - Compilation: `CompiledMethodLoad`, `CompiledMethodUnload`, `DynamicCodeGenerated`
712/// - Exceptions: `Exception`, `ExceptionCatch`
713/// - Debugging: `SingleStep`, `Breakpoint`, `FramePop`
714/// - Monitors: `MonitorWait`, `MonitorWaited`, `MonitorContendedEnter`, `MonitorContendedEntered`
715/// - Fields: `FieldAccess`, `FieldModification`
716/// - GC: `GarbageCollectionStart`, `GarbageCollectionFinish`, `ResourceExhausted`
717/// - Objects: `ObjectFree`, `VMObjectAlloc`, `SampledObjectAlloc`
718pub fn get_default_callbacks() -> jvmti::jvmtiEventCallbacks {
719
720    let mut callbacks = jvmti::jvmtiEventCallbacks::default();
721
722    callbacks.VMInit = Some(trampoline_vm_init);
723    callbacks.VMDeath = Some(trampoline_vm_death);
724    callbacks.VMStart = Some(trampoline_vm_start);
725
726    callbacks.ThreadStart = Some(trampoline_thread_start);
727    callbacks.ThreadEnd = Some(trampoline_thread_end);
728
729    callbacks.ClassLoad = Some(trampoline_class_load);
730    callbacks.ClassPrepare = Some(trampoline_class_prepare);
731    callbacks.ClassFileLoadHook = Some(trampoline_class_file_load_hook);
732
733    callbacks.Exception = Some(trampoline_exception);
734    callbacks.ExceptionCatch = Some(trampoline_exception_catch);
735
736    callbacks.SingleStep = Some(trampoline_single_step);
737    callbacks.Breakpoint = Some(trampoline_breakpoint);
738    callbacks.FramePop = Some(trampoline_frame_pop);
739
740    callbacks.FieldAccess = Some(trampoline_field_access);
741    callbacks.FieldModification = Some(trampoline_field_modification);
742
743    callbacks.MethodEntry = Some(trampoline_method_entry);
744    callbacks.MethodExit = Some(trampoline_method_exit);
745    callbacks.NativeMethodBind = Some(trampoline_native_method_bind);
746
747    callbacks.CompiledMethodLoad = Some(trampoline_compiled_method_load);
748    callbacks.CompiledMethodUnload = Some(trampoline_compiled_method_unload);
749    callbacks.DynamicCodeGenerated = Some(trampoline_dynamic_code_generated);
750
751    callbacks.MonitorWait = Some(trampoline_monitor_wait);
752    callbacks.MonitorWaited = Some(trampoline_monitor_waited);
753    callbacks.MonitorContendedEnter = Some(trampoline_monitor_contended_enter);
754    callbacks.MonitorContendedEntered = Some(trampoline_monitor_contended_entered);
755
756    callbacks.GarbageCollectionStart = Some(trampoline_garbage_collection_start);
757    callbacks.GarbageCollectionFinish = Some(trampoline_garbage_collection_finish);
758    callbacks.ResourceExhausted = Some(trampoline_resource_exhausted);
759
760    callbacks.ObjectFree = Some(trampoline_object_free);
761    callbacks.VMObjectAlloc = Some(trampoline_vm_object_alloc);
762    callbacks.SampledObjectAlloc = Some(trampoline_sampled_object_alloc);
763
764    callbacks
765}
766
767
768/// Exports your agent type as a loadable JVMTI agent library.
769///
770/// This macro generates the required `Agent_OnLoad` and `Agent_OnUnload` FFI entry points
771/// that the JVM expects when loading an agent via `-agentpath` or `-agentlib`.
772///
773/// # Requirements
774///
775/// Your agent type must implement:
776/// - [`Agent`] trait - for handling JVMTI events
777/// - [`Default`] trait - for instantiation (the macro calls `<YourType>::default()`)
778/// - [`Sync`] + [`Send`] - for thread-safe event handling (enforced by `Agent` trait bounds)
779///
780/// # Generated Functions
781///
782/// The macro generates two `extern "system"` functions:
783///
784/// - **`Agent_OnLoad`**: Called by the JVM when the agent is loaded. Creates your agent
785///   instance, registers it globally, and calls your [`Agent::on_load`] method.
786///
787/// - **`Agent_OnUnload`**: Called by the JVM during shutdown. Calls your [`Agent::on_unload`]
788///   method for cleanup.
789///
790/// # Example
791///
792/// ```rust,ignore
793/// use jvmti_bindings::prelude::*;
794///
795/// #[derive(Default)]
796/// struct MyAgent {
797///     // Your agent state here
798/// }
799///
800/// impl Agent for MyAgent {
801///     fn on_load(&self, vm: *mut jni::JavaVM, options: &str) -> jni::jint {
802///         println!("Agent loaded with options: {}", options);
803///         jni::JNI_OK
804///     }
805/// }
806///
807/// // This generates Agent_OnLoad and Agent_OnUnload
808/// export_agent!(MyAgent);
809/// ```
810///
811/// # Building
812///
813/// Your crate must be built as a C dynamic library. Add to `Cargo.toml`:
814///
815/// ```toml
816/// [lib]
817/// crate-type = ["cdylib"]
818/// ```
819///
820/// # Loading the Agent
821///
822/// ```bash
823/// # Build your agent
824/// cargo build --release
825///
826/// # Load with JVM (Linux)
827/// java -agentpath:./target/release/libmyagent.so=option1,option2 MyApp
828///
829/// # Load with JVM (macOS)
830/// java -agentpath:./target/release/libmyagent.dylib=option1,option2 MyApp
831///
832/// # Load with JVM (Windows)
833/// java -agentpath:./target/release/myagent.dll=option1,option2 MyApp
834/// ```
835///
836/// # Options String
837///
838/// The options string (everything after `=` in `-agentpath`) is passed to your
839/// [`Agent::on_load`] method. Parse it however you like - common patterns include
840/// comma-separated key=value pairs or simple flags.
841///
842/// # Thread Safety Notes
843///
844/// - Only one agent instance is created per JVM (stored in a global `OnceLock`)
845/// - Your agent's methods may be called concurrently from multiple JVM threads
846/// - Use interior mutability (`Mutex`, `RwLock`, `AtomicXxx`) for mutable state
847///
848/// # Return Values
849///
850/// Your `on_load` must return:
851/// - [`jni::JNI_OK`] (0) on success - JVM continues loading
852/// - [`jni::JNI_ERR`] (-1) on failure - JVM aborts startup with an error
853#[macro_export]
854macro_rules! export_agent {
855    ($agent_type:ty) => {
856        #[no_mangle]
857        pub unsafe extern "system" fn Agent_OnLoad(
858            vm: *mut $crate::sys::jni::JavaVM,
859            options: *mut std::ffi::c_char,
860            reserved: *mut std::ffi::c_void,
861        ) -> $crate::sys::jni::jint {
862
863            // 1. Create and Register the Agent
864            let agent = Box::new(<$agent_type>::default());
865            if let Err(_) = $crate::set_global_agent(agent) {
866                return $crate::sys::jni::JNI_ERR;
867            }
868
869            // 2. Handle Options
870            let options_str = if options.is_null() {
871                ""
872            } else {
873                std::ffi::CStr::from_ptr(options).to_str().unwrap_or("")
874            };
875
876            // 3. Call the User's Logic
877            if let Some(global_agent) = $crate::GLOBAL_AGENT.get() {
878                return global_agent.on_load(vm, options_str);
879            }
880
881            $crate::sys::jni::JNI_ERR
882        }
883
884        #[no_mangle]
885        pub unsafe extern "system" fn Agent_OnAttach(
886            vm: *mut $crate::sys::jni::JavaVM,
887            options: *mut std::ffi::c_char,
888            reserved: *mut std::ffi::c_void,
889        ) -> $crate::sys::jni::jint {
890
891            // 1. Create and Register the Agent
892            let agent = Box::new(<$agent_type>::default());
893            if let Err(_) = $crate::set_global_agent(agent) {
894                return $crate::sys::jni::JNI_ERR;
895            }
896
897            // 2. Handle Options
898            let options_str = if options.is_null() {
899                ""
900            } else {
901                std::ffi::CStr::from_ptr(options).to_str().unwrap_or("")
902            };
903
904            // 3. Call the User's Logic
905            if let Some(global_agent) = $crate::GLOBAL_AGENT.get() {
906                return global_agent.on_attach(vm, options_str);
907            }
908
909            $crate::sys::jni::JNI_ERR
910        }
911
912        #[no_mangle]
913        pub unsafe extern "system" fn Agent_OnUnload(vm: *mut $crate::sys::jni::JavaVM) {
914             if let Some(agent) = $crate::GLOBAL_AGENT.get() {
915                agent.on_unload();
916            }
917        }
918    };
919}