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:
parent
e1b59ec736
commit
0df04f2129
3 changed files with 98 additions and 0 deletions
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue