diff --git a/src/binding.cc b/src/binding.cc index 9cfdf07323..e7eaaceb16 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -349,6 +349,11 @@ bool v8__Isolate__GetHeapSpaceStatistics( return isolate->GetHeapSpaceStatistics(space_statistics, index); } +bool v8__Isolate__GetHeapCodeAndMetadataStatistics(v8::Isolate* isolate, + v8::HeapCodeStatistics* s) { + return isolate->GetHeapCodeAndMetadataStatistics(s); +} + void v8__Isolate__RemoveNearHeapLimitCallback( v8::Isolate* isolate, v8::NearHeapLimitCallback callback, size_t heap_limit) { diff --git a/src/binding.hpp b/src/binding.hpp index fb2d5dcc90..ff3ded95bb 100644 --- a/src/binding.hpp +++ b/src/binding.hpp @@ -42,6 +42,7 @@ using v8__String__WriteFlags = v8::String::WriteFlags; using v8__ModuleImportPhase = v8::ModuleImportPhase; using v8__HeapStatistics = v8::HeapStatistics; using v8__HeapSpaceStatistics = v8::HeapSpaceStatistics; +using v8__HeapCodeStatistics = v8::HeapCodeStatistics; using v8__GCType = v8::GCType; using v8__GCCallbackFlags = v8::GCCallbackFlags; using v8__Intercepted = v8::Intercepted; diff --git a/src/isolate.rs b/src/isolate.rs index 42e1b63f5a..c833505130 100644 --- a/src/isolate.rs +++ b/src/isolate.rs @@ -18,6 +18,7 @@ use crate::StartupData; use crate::String; use crate::V8::get_current_platform; use crate::Value; +use crate::binding::v8__HeapCodeStatistics; use crate::binding::v8__HeapSpaceStatistics; use crate::binding::v8__HeapStatistics; use crate::binding::v8__Isolate__UseCounterFeature; @@ -666,6 +667,10 @@ unsafe extern "C" { space_statistics: *mut v8__HeapSpaceStatistics, index: size_t, ) -> bool; + fn v8__Isolate__GetHeapCodeAndMetadataStatistics( + isolate: *mut RealIsolate, + code_statistics: *mut v8__HeapCodeStatistics, + ) -> bool; fn v8__Isolate__AddNearHeapLimitCallback( isolate: *mut RealIsolate, callback: NearHeapLimitCallback, @@ -1269,6 +1274,26 @@ impl Isolate { Some(HeapSpaceStatistics(inner)) } + /// Get code and metadata statistics for the heap. + /// + /// \returns true on success. + #[inline(always)] + pub fn get_heap_code_and_metadata_statistics( + &mut self, + ) -> Option { + let inner = unsafe { + let mut s = MaybeUninit::zeroed(); + if !v8__Isolate__GetHeapCodeAndMetadataStatistics( + self.as_real_ptr(), + s.as_mut_ptr(), + ) { + return None; + } + s.assume_init() + }; + Some(HeapCodeStatistics(inner)) + } + /// Tells V8 to capture current stack trace when uncaught exception occurs /// and report it to the message listeners. The option is off by default. #[inline(always)] @@ -2234,6 +2259,26 @@ impl HeapSpaceStatistics { } } +pub struct HeapCodeStatistics(v8__HeapCodeStatistics); + +impl HeapCodeStatistics { + pub fn code_and_metadata_size(&self) -> usize { + self.0.code_and_metadata_size_ + } + + pub fn bytecode_and_metadata_size(&self) -> usize { + self.0.bytecode_and_metadata_size_ + } + + pub fn external_script_source_size(&self) -> usize { + self.0.external_script_source_size_ + } + + pub fn cpu_profiler_metadata_size(&self) -> usize { + self.0.cpu_profiler_metadata_size_ + } +} + impl<'s, F> MapFnFrom for PrepareStackTraceCallback<'s> where F: UnitType diff --git a/src/lib.rs b/src/lib.rs index 305ea2cd3a..a975ff8554 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,6 +106,7 @@ pub use handle::SealedLocal; pub use handle::TracedReference; pub use handle::Weak; pub use isolate::GarbageCollectionType; +pub use isolate::HeapCodeStatistics; pub use isolate::HeapSpaceStatistics; pub use isolate::HeapStatistics; pub use isolate::HostCreateShadowRealmContextCallback; diff --git a/tests/test_api.rs b/tests/test_api.rs index 429319e7d1..939f5a0e79 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -7792,6 +7792,42 @@ fn heap_statistics() { assert_ne!(s.number_of_native_contexts(), 0); } +#[test] +fn heap_code_statistics() { + let _setup_guard = setup::parallel_test(); + + let isolate = &mut v8::Isolate::new(Default::default()); + + // Before running any code, statistics should still be available. + let s = isolate + .get_heap_code_and_metadata_statistics() + .expect("get_heap_code_and_metadata_statistics should succeed"); + + // Before running any code, code_and_metadata_size may or may not be > 0 + // depending on the platform and V8 version. + // external_script_source_size and cpu_profiler_metadata_size start at 0. + assert_eq!(s.external_script_source_size(), 0); + assert_eq!(s.cpu_profiler_metadata_size(), 0); + + // Run some JS to generate bytecode. + { + v8::scope!(let scope, isolate); + let context = v8::Context::new(scope, Default::default()); + let scope = &mut v8::ContextScope::new(scope, context); + eval(scope, "function foo() { return 1 + 2; } foo();").unwrap(); + } + + let s2 = isolate + .get_heap_code_and_metadata_statistics() + .expect("get_heap_code_and_metadata_statistics should succeed"); + + // After compiling code, bytecode_and_metadata_size should increase. + assert!(s2.bytecode_and_metadata_size() > 0); + // code_and_metadata_size tracks JIT-compiled code, which may be 0 if V8 + // only uses the interpreter for simple scripts. + assert!(s2.code_and_metadata_size() >= s.code_and_metadata_size()); +} + #[test] fn low_memory_notification() { let _setup_guard = setup::parallel_test();