Skip to content

Commit 31352ff

Browse files
bartlomiejuclaude
andcommitted
feat: add HeapCodeStatistics API
Expose `Isolate::get_heap_code_and_metadata_statistics()` which wraps V8's `GetHeapCodeAndMetadataStatistics`. Returns a `HeapCodeStatistics` struct with code_and_metadata_size, bytecode_and_metadata_size, external_script_source_size, and cpu_profiler_metadata_size. This is needed to implement Node.js `v8.getHeapCodeStatistics()` in Deno. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 154d2f2 commit 31352ff

File tree

4 files changed

+105
-0
lines changed

4 files changed

+105
-0
lines changed

src/binding.cc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,26 @@ bool v8__Isolate__GetHeapSpaceStatistics(
349349
return isolate->GetHeapSpaceStatistics(space_statistics, index);
350350
}
351351

352+
struct v8__HeapCodeStatistics {
353+
size_t code_and_metadata_size_;
354+
size_t bytecode_and_metadata_size_;
355+
size_t external_script_source_size_;
356+
size_t cpu_profiler_metadata_size_;
357+
};
358+
359+
bool v8__Isolate__GetHeapCodeAndMetadataStatistics(v8::Isolate* isolate,
360+
v8__HeapCodeStatistics* s) {
361+
v8::HeapCodeStatistics stats;
362+
bool ok = isolate->GetHeapCodeAndMetadataStatistics(&stats);
363+
if (ok) {
364+
s->code_and_metadata_size_ = stats.code_and_metadata_size();
365+
s->bytecode_and_metadata_size_ = stats.bytecode_and_metadata_size();
366+
s->external_script_source_size_ = stats.external_script_source_size();
367+
s->cpu_profiler_metadata_size_ = stats.cpu_profiler_metadata_size();
368+
}
369+
return ok;
370+
}
371+
352372
void v8__Isolate__RemoveNearHeapLimitCallback(
353373
v8::Isolate* isolate, v8::NearHeapLimitCallback callback,
354374
size_t heap_limit) {

src/isolate.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,10 @@ unsafe extern "C" {
666666
space_statistics: *mut v8__HeapSpaceStatistics,
667667
index: size_t,
668668
) -> bool;
669+
fn v8__Isolate__GetHeapCodeAndMetadataStatistics(
670+
isolate: *mut RealIsolate,
671+
code_statistics: *mut HeapCodeStatistics,
672+
) -> bool;
669673
fn v8__Isolate__AddNearHeapLimitCallback(
670674
isolate: *mut RealIsolate,
671675
callback: NearHeapLimitCallback,
@@ -1269,6 +1273,25 @@ impl Isolate {
12691273
Some(HeapSpaceStatistics(inner))
12701274
}
12711275

1276+
/// Get code and metadata statistics for the heap.
1277+
///
1278+
/// \returns true on success.
1279+
#[inline(always)]
1280+
pub fn get_heap_code_and_metadata_statistics(
1281+
&mut self,
1282+
) -> Option<HeapCodeStatistics> {
1283+
unsafe {
1284+
let mut s = MaybeUninit::zeroed();
1285+
if !v8__Isolate__GetHeapCodeAndMetadataStatistics(
1286+
self.as_real_ptr(),
1287+
s.as_mut_ptr(),
1288+
) {
1289+
return None;
1290+
}
1291+
Some(s.assume_init())
1292+
}
1293+
}
1294+
12721295
/// Tells V8 to capture current stack trace when uncaught exception occurs
12731296
/// and report it to the message listeners. The option is off by default.
12741297
#[inline(always)]
@@ -2234,6 +2257,32 @@ impl HeapSpaceStatistics {
22342257
}
22352258
}
22362259

2260+
#[repr(C)]
2261+
pub struct HeapCodeStatistics {
2262+
code_and_metadata_size_: usize,
2263+
bytecode_and_metadata_size_: usize,
2264+
external_script_source_size_: usize,
2265+
cpu_profiler_metadata_size_: usize,
2266+
}
2267+
2268+
impl HeapCodeStatistics {
2269+
pub fn code_and_metadata_size(&self) -> usize {
2270+
self.code_and_metadata_size_
2271+
}
2272+
2273+
pub fn bytecode_and_metadata_size(&self) -> usize {
2274+
self.bytecode_and_metadata_size_
2275+
}
2276+
2277+
pub fn external_script_source_size(&self) -> usize {
2278+
self.external_script_source_size_
2279+
}
2280+
2281+
pub fn cpu_profiler_metadata_size(&self) -> usize {
2282+
self.cpu_profiler_metadata_size_
2283+
}
2284+
}
2285+
22372286
impl<'s, F> MapFnFrom<F> for PrepareStackTraceCallback<'s>
22382287
where
22392288
F: UnitType

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub use handle::SealedLocal;
106106
pub use handle::TracedReference;
107107
pub use handle::Weak;
108108
pub use isolate::GarbageCollectionType;
109+
pub use isolate::HeapCodeStatistics;
109110
pub use isolate::HeapSpaceStatistics;
110111
pub use isolate::HeapStatistics;
111112
pub use isolate::HostCreateShadowRealmContextCallback;

tests/test_api.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7792,6 +7792,41 @@ fn heap_statistics() {
77927792
assert_ne!(s.number_of_native_contexts(), 0);
77937793
}
77947794

7795+
#[test]
7796+
fn heap_code_statistics() {
7797+
let _setup_guard = setup::parallel_test();
7798+
7799+
let isolate = &mut v8::Isolate::new(Default::default());
7800+
7801+
// Before running any code, statistics should still be available.
7802+
let s = isolate
7803+
.get_heap_code_and_metadata_statistics()
7804+
.expect("get_heap_code_and_metadata_statistics should succeed");
7805+
7806+
// After creating an isolate, there should be some code already compiled.
7807+
assert!(s.code_and_metadata_size() > 0);
7808+
// Bytecode may or may not be present at this point.
7809+
// external_script_source_size and cpu_profiler_metadata_size start at 0.
7810+
assert_eq!(s.external_script_source_size(), 0);
7811+
assert_eq!(s.cpu_profiler_metadata_size(), 0);
7812+
7813+
// Run some JS to generate bytecode.
7814+
{
7815+
v8::scope!(let scope, isolate);
7816+
let context = v8::Context::new(scope, Default::default());
7817+
let scope = &mut v8::ContextScope::new(scope, context);
7818+
eval(scope, "function foo() { return 1 + 2; } foo();").unwrap();
7819+
}
7820+
7821+
let s2 = isolate
7822+
.get_heap_code_and_metadata_statistics()
7823+
.expect("get_heap_code_and_metadata_statistics should succeed");
7824+
7825+
// After compiling code, bytecode_and_metadata_size should increase.
7826+
assert!(s2.bytecode_and_metadata_size() > 0);
7827+
assert!(s2.code_and_metadata_size() > 0);
7828+
}
7829+
77957830
#[test]
77967831
fn low_memory_notification() {
77977832
let _setup_guard = setup::parallel_test();

0 commit comments

Comments
 (0)