Skip to content

Commit

Permalink
deps: V8: cherry-pick 0fd478bcdabd
Browse files Browse the repository at this point in the history
Original commit message:

    [heap-profiler]: expose QueryObjects() to v8::HeapProfiler

    This allows embedders to use this API for testing memory leaks more
    reliably. See nodejs#50572 for an
    example about how the API can be used.

    Change-Id: Ic3d1268e2b331c37e8ec92997b764b9b5486f8c2
    Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5006373
    Reviewed-by: Camillo Bruni <cbruni@chromium.org>
    Reviewed-by: Simon Zünd <szuend@chromium.org>
    Commit-Queue: Joyee Cheung <joyee@igalia.com>
    Cr-Commit-Position: refs/heads/main@{#91123}

Refs: v8/v8@0fd478b
PR-URL: nodejs#50572
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
  • Loading branch information
joyeecheung authored and targos committed Dec 21, 2023
1 parent edd6431 commit 589fa3b
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 26 deletions.
2 changes: 1 addition & 1 deletion common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
'v8_embedder_string': '-node.10',
'v8_embedder_string': '-node.11',

##### V8 defaults for Node.js #####

Expand Down
10 changes: 10 additions & 0 deletions deps/v8/include/v8-profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -921,12 +921,22 @@ class V8_EXPORT EmbedderGraph {
virtual ~EmbedderGraph() = default;
};

class QueryObjectPredicate {
public:
virtual ~QueryObjectPredicate() = default;
virtual bool Filter(v8::Local<v8::Object> object) = 0;
};

/**
* Interface for controlling heap profiling. Instance of the
* profiler can be retrieved using v8::Isolate::GetHeapProfiler.
*/
class V8_EXPORT HeapProfiler {
public:
void QueryObjects(v8::Local<v8::Context> context,
QueryObjectPredicate* predicate,
std::vector<v8::Global<v8::Object>>* objects);

enum SamplingFlags {
kSamplingNoFlags = 0,
kSamplingForceGC = 1 << 0,
Expand Down
10 changes: 10 additions & 0 deletions deps/v8/src/api/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11134,6 +11134,16 @@ int HeapProfiler::GetSnapshotCount() {
return reinterpret_cast<i::HeapProfiler*>(this)->GetSnapshotsCount();
}

void HeapProfiler::QueryObjects(Local<Context> v8_context,
QueryObjectPredicate* predicate,
std::vector<Global<Object>>* objects) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_context->GetIsolate());
i::HeapProfiler* profiler = reinterpret_cast<i::HeapProfiler*>(this);
DCHECK_EQ(isolate, profiler->isolate());
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
profiler->QueryObjects(Utils::OpenHandle(*v8_context), predicate, objects);
}

const HeapSnapshot* HeapProfiler::GetHeapSnapshot(int index) {
return reinterpret_cast<const HeapSnapshot*>(
reinterpret_cast<i::HeapProfiler*>(this)->GetSnapshot(index));
Expand Down
9 changes: 0 additions & 9 deletions deps/v8/src/debug/debug-interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1211,15 +1211,6 @@ v8::MaybeLocal<v8::Value> EvaluateGlobalForTesting(
RETURN_ESCAPED(result);
}

void QueryObjects(v8::Local<v8::Context> v8_context,
QueryObjectPredicate* predicate,
std::vector<v8::Global<v8::Object>>* objects) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_context->GetIsolate());
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
isolate->heap_profiler()->QueryObjects(Utils::OpenHandle(*v8_context),
predicate, objects);
}

void GlobalLexicalScopeNames(v8::Local<v8::Context> v8_context,
std::vector<v8::Global<v8::String>>* names) {
i::Handle<i::Context> context = Utils::OpenHandle(*v8_context);
Expand Down
10 changes: 0 additions & 10 deletions deps/v8/src/debug/debug-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,16 +530,6 @@ class V8_EXPORT_PRIVATE StackTraceIterator {
bool throw_on_side_effect) = 0;
};

class QueryObjectPredicate {
public:
virtual ~QueryObjectPredicate() = default;
virtual bool Filter(v8::Local<v8::Object> object) = 0;
};

void QueryObjects(v8::Local<v8::Context> context,
QueryObjectPredicate* predicate,
std::vector<v8::Global<v8::Object>>* objects);

void GlobalLexicalScopeNames(v8::Local<v8::Context> context,
std::vector<v8::Global<v8::String>>* names);

Expand Down
5 changes: 3 additions & 2 deletions deps/v8/src/inspector/v8-debugger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "include/v8-context.h"
#include "include/v8-function.h"
#include "include/v8-microtask-queue.h"
#include "include/v8-profiler.h"
#include "include/v8-util.h"
#include "src/inspector/inspected-context.h"
#include "src/inspector/protocol/Protocol.h"
Expand Down Expand Up @@ -38,7 +39,7 @@ void cleanupExpiredWeakPointers(Map& map) {
}
}

class MatchPrototypePredicate : public v8::debug::QueryObjectPredicate {
class MatchPrototypePredicate : public v8::QueryObjectPredicate {
public:
MatchPrototypePredicate(V8InspectorImpl* inspector,
v8::Local<v8::Context> context,
Expand Down Expand Up @@ -994,7 +995,7 @@ v8::Local<v8::Array> V8Debugger::queryObjects(v8::Local<v8::Context> context,
v8::Isolate* isolate = context->GetIsolate();
std::vector<v8::Global<v8::Object>> v8_objects;
MatchPrototypePredicate predicate(m_inspector, context, prototype);
v8::debug::QueryObjects(context, &predicate, &v8_objects);
isolate->GetHeapProfiler()->QueryObjects(context, &predicate, &v8_objects);

v8::MicrotasksScope microtasksScope(context,
v8::MicrotasksScope::kDoNotRunMicrotasks);
Expand Down
2 changes: 1 addition & 1 deletion deps/v8/src/profiler/heap-profiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ Heap* HeapProfiler::heap() const { return ids_->heap(); }
Isolate* HeapProfiler::isolate() const { return heap()->isolate(); }

void HeapProfiler::QueryObjects(Handle<Context> context,
debug::QueryObjectPredicate* predicate,
v8::QueryObjectPredicate* predicate,
std::vector<v8::Global<v8::Object>>* objects) {
// We need a stack marker here to allow deterministic passes over the stack.
// The garbage collection and the two object heap iterators should scan the
Expand Down
5 changes: 2 additions & 3 deletions deps/v8/src/profiler/heap-profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class StringsStorage;
// generate consistent IDs for moved objects.
class HeapProfilerNativeMoveListener {
public:
HeapProfilerNativeMoveListener(HeapProfiler* profiler)
explicit HeapProfilerNativeMoveListener(HeapProfiler* profiler)
: profiler_(profiler) {}
HeapProfilerNativeMoveListener(const HeapProfilerNativeMoveListener& other) =
delete;
Expand Down Expand Up @@ -119,8 +119,7 @@ class HeapProfiler : public HeapObjectAllocationTracker {

Isolate* isolate() const;

void QueryObjects(Handle<Context> context,
debug::QueryObjectPredicate* predicate,
void QueryObjects(Handle<Context> context, QueryObjectPredicate* predicate,
std::vector<v8::Global<v8::Object>>* objects);
void set_native_move_listener(
std::unique_ptr<HeapProfilerNativeMoveListener> listener) {
Expand Down
62 changes: 62 additions & 0 deletions deps/v8/test/cctest/test-heap-profiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <ctype.h>

#include <memory>
#include <vector>

#include "include/v8-function.h"
#include "include/v8-json.h"
Expand Down Expand Up @@ -4068,6 +4069,67 @@ TEST(SamplingHeapProfilerSampleDuringDeopt) {
heap_profiler->StopSamplingHeapProfiler();
}

namespace {
class TestQueryObjectPredicate : public v8::QueryObjectPredicate {
public:
TestQueryObjectPredicate(v8::Local<v8::Context> context,
v8::Local<v8::Symbol> symbol)
: context_(context), symbol_(symbol) {}

bool Filter(v8::Local<v8::Object> object) override {
return object->HasOwnProperty(context_, symbol_).FromMaybe(false);
}

private:
v8::Local<v8::Context> context_;
v8::Local<v8::Symbol> symbol_;
};

class IncludeAllQueryObjectPredicate : public v8::QueryObjectPredicate {
public:
IncludeAllQueryObjectPredicate() {}
bool Filter(v8::Local<v8::Object> object) override { return true; }
};
} // anonymous namespace

TEST(QueryObjects) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = env.local();

v8::Local<v8::Symbol> sym =
v8::Symbol::New(isolate, v8_str("query_object_test"));
context->Global()->Set(context, v8_str("test_symbol"), sym).Check();
v8::Local<v8::Value> arr = CompileRun(R"(
const arr = [];
for (let i = 0; i < 10; ++i) {
arr.push({[test_symbol]: true});
}
arr;
)");
context->Global()->Set(context, v8_str("arr"), arr).Check();
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();

{
TestQueryObjectPredicate predicate(context, sym);
std::vector<v8::Global<v8::Object>> out;
heap_profiler->QueryObjects(context, &predicate, &out);

CHECK_EQ(out.size(), 10);
for (size_t i = 0; i < out.size(); ++i) {
CHECK(out[i].Get(isolate)->HasOwnProperty(context, sym).FromMaybe(false));
}
}

{
IncludeAllQueryObjectPredicate predicate;
std::vector<v8::Global<v8::Object>> out;
heap_profiler->QueryObjects(context, &predicate, &out);
CHECK_GE(out.size(), 10);
}
}

TEST(WeakReference) {
v8::Isolate* isolate = CcTest::isolate();
i::Isolate* i_isolate = CcTest::i_isolate();
Expand Down

0 comments on commit 589fa3b

Please sign in to comment.