0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-12-23 15:50:11 -05:00

Add Isolate::take_heap_snapshot() (#302)

This doesn't really follow the current V8 API (it's pretty close to how
V8 used to be back in 2012 though.) However:

1. The C++ API is very C++-y and doesn't carry over well to Rust, and
2. It addresses the immediate need of being able to take heap snapshots.

Refs #298
This commit is contained in:
Ben Noordhuis 2020-03-09 18:30:25 +01:00 committed by GitHub
parent e1b59ec736
commit 0df04f2129
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 0 deletions

View file

@ -6,6 +6,7 @@
#include "v8/include/libplatform/libplatform.h"
#include "v8/include/v8-inspector.h"
#include "v8/include/v8-platform.h"
#include "v8/include/v8-profiler.h"
#include "v8/include/v8.h"
using namespace support;
@ -1572,4 +1573,38 @@ v8::Value* v8__Module__Evaluate(v8::Module& self,
return maybe_local_to_ptr(self.Evaluate(context));
}
using HeapSnapshotCallback = bool (*)(void*, const char*, size_t);
void v8__HeapProfiler__TakeHeapSnapshot(v8::Isolate* isolate,
HeapSnapshotCallback callback,
void* arg) {
struct OutputStream : public v8::OutputStream {
OutputStream(HeapSnapshotCallback callback, void* arg)
: callback_(callback), arg_(arg) {}
void EndOfStream() override {
static_cast<void>(callback_(arg_, nullptr, 0));
}
v8::OutputStream::WriteResult WriteAsciiChunk(char* data,
int size) override {
assert(size >= 0); // Can never be < 0 barring bugs in V8.
if (callback_(arg_, data, static_cast<size_t>(size)))
return v8::OutputStream::kContinue;
return v8::OutputStream::kAbort;
}
HeapSnapshotCallback const callback_;
void* const arg_;
};
const v8::HeapSnapshot* snapshot =
isolate->GetHeapProfiler()->TakeHeapSnapshot();
if (snapshot == nullptr) return; // Snapshotting failed, probably OOM.
OutputStream stream(callback, arg);
snapshot->Serialize(&stream);
// We don't want to call HeapProfiler::DeleteAllHeapSnapshots() because that
// invalidates snapshots we don't own. The const_cast hack has been in use
// in node-heapdump for the last 8 years and I think there is a pretty
// good chance it'll keep working for 8 more.
const_cast<v8::HeapSnapshot*>(snapshot)->Delete();
}
} // extern "C"

View file

@ -132,6 +132,11 @@ extern "C" {
this: &mut CreateParams,
snapshot_blob: *mut StartupData,
);
fn v8__HeapProfiler__TakeHeapSnapshot(
isolate: *mut Isolate,
callback: extern "C" fn(*mut c_void, *const u8, usize) -> bool,
arg: *mut c_void,
);
}
#[repr(C)]
@ -299,6 +304,34 @@ impl Isolate {
// deno where dropping Annex before the states causes a segfault.
v8__Isolate__Dispose(self)
}
/// Take a heap snapshot. The callback is invoked one or more times
/// with byte slices containing the snapshot serialized as JSON.
/// It's the callback's responsibility to reassemble them into
/// a single document, e.g., by writing them to a file.
/// Note that Chrome DevTools refuses to load snapshots without
/// a .heapsnapshot suffix.
pub fn take_heap_snapshot<F>(&mut self, mut callback: F)
where
F: FnMut(&[u8]) -> bool,
{
extern "C" fn trampoline<F>(
arg: *mut c_void,
data: *const u8,
size: usize,
) -> bool
where
F: FnMut(&[u8]) -> bool,
{
let p = arg as *mut F;
let callback = unsafe { &mut *p };
let slice = unsafe { std::slice::from_raw_parts(data, size) };
callback(slice)
}
let arg = &mut callback as *mut F as *mut c_void;
unsafe { v8__HeapProfiler__TakeHeapSnapshot(self, trampoline::<F>, arg) }
}
}
pub(crate) struct IsolateAnnex {

View file

@ -2752,3 +2752,33 @@ fn get_and_set_data() {
assert_eq!(*b, 123);
}
}
#[test]
fn take_heap_snapshot() {
let _setup_guard = setup();
let mut params = v8::Isolate::create_params();
params.set_array_buffer_allocator(v8::new_default_allocator());
let mut isolate = v8::Isolate::new(params);
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source = r#"
{
class Eyecatcher {}
const eyecatchers = globalThis.eyecatchers = [];
for (let i = 0; i < 1e4; i++) eyecatchers.push(new Eyecatcher);
}
"#;
let _ = eval(scope, context, source).unwrap();
let mut vec = Vec::<u8>::new();
isolate.take_heap_snapshot(|chunk| {
vec.extend_from_slice(chunk);
true
});
let s = std::str::from_utf8(&vec).unwrap();
assert!(s.find(r#""Eyecatcher""#).is_some());
}
}