Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fa25e06

Browse filesBrowse files
joyeecheungRafaelGSS
authored andcommitted
src: implement countObjectsWithPrototype
This implements an internal utility for counting objects in the heap with a specified prototype. In addition this adds a checkIfCollectableByCounting() test helper. PR-URL: #50572 Refs: v8/v8@0fd478b Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
1 parent 8da9d96 commit fa25e06
Copy full SHA for fa25e06

File tree

Expand file treeCollapse file tree

2 files changed

+83
-0
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

2 files changed

+83
-0
lines changed
Open diff view settings
Collapse file

‎src/heap_utils.cc‎

Copy file name to clipboardExpand all lines: src/heap_utils.cc
+36Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,39 @@ void TriggerHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
474474
return args.GetReturnValue().Set(filename_v);
475475
}
476476

477+
class PrototypeChainHas : public v8::QueryObjectPredicate {
478+
public:
479+
PrototypeChainHas(Local<Context> context, Local<Object> search)
480+
: context_(context), search_(search) {}
481+
482+
// What we can do in the filter can be quite limited, but looking up
483+
// the prototype chain is something that the inspector console API
484+
// queryObject() does so it is supported.
485+
bool Filter(Local<Object> object) override {
486+
for (Local<Value> proto = object->GetPrototype(); proto->IsObject();
487+
proto = proto.As<Object>()->GetPrototype()) {
488+
if (search_ == proto) return true;
489+
}
490+
return false;
491+
}
492+
493+
private:
494+
Local<Context> context_;
495+
Local<Object> search_;
496+
};
497+
498+
void CountObjectsWithPrototype(const FunctionCallbackInfo<Value>& args) {
499+
CHECK_EQ(args.Length(), 1);
500+
CHECK(args[0]->IsObject());
501+
Local<Object> proto = args[0].As<Object>();
502+
Isolate* isolate = args.GetIsolate();
503+
Local<Context> context = isolate->GetCurrentContext();
504+
PrototypeChainHas prototype_chain_has(context, proto);
505+
std::vector<Global<Object>> out;
506+
isolate->GetHeapProfiler()->QueryObjects(context, &prototype_chain_has, &out);
507+
args.GetReturnValue().Set(static_cast<uint32_t>(out.size()));
508+
}
509+
477510
void Initialize(Local<Object> target,
478511
Local<Value> unused,
479512
Local<Context> context,
@@ -482,12 +515,15 @@ void Initialize(Local<Object> target,
482515
SetMethod(context, target, "triggerHeapSnapshot", TriggerHeapSnapshot);
483516
SetMethod(
484517
context, target, "createHeapSnapshotStream", CreateHeapSnapshotStream);
518+
SetMethod(
519+
context, target, "countObjectsWithPrototype", CountObjectsWithPrototype);
485520
}
486521

487522
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
488523
registry->Register(BuildEmbedderGraph);
489524
registry->Register(TriggerHeapSnapshot);
490525
registry->Register(CreateHeapSnapshotStream);
526+
registry->Register(CountObjectsWithPrototype);
491527
}
492528

493529
} // namespace heap
Collapse file

‎test/common/gc.js‎

Copy file name to clipboardExpand all lines: test/common/gc.js
+47Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,54 @@ async function runAndBreathe(fn, repeat, waitTime = 20) {
7575
}
7676
}
7777

78+
/**
79+
* This requires --expose-internals.
80+
* This function can be used to check if an object factory leaks or not by
81+
* iterating over the heap and count objects with the specified class
82+
* (which is checked by looking up the prototype chain).
83+
* @param {(i: number) => number} fn The factory receiving iteration count
84+
* and returning number of objects created. The return value should be
85+
* precise otherwise false negatives can be produced.
86+
* @param {Function} klass The class whose object is used to count the objects
87+
* @param {number} count Number of iterations that this check should be done
88+
* @param {number} waitTime Optional breathing time for GC.
89+
*/
90+
async function checkIfCollectableByCounting(fn, klass, count, waitTime = 20) {
91+
const { internalBinding } = require('internal/test/binding');
92+
const { countObjectsWithPrototype } = internalBinding('heap_utils');
93+
const { prototype, name } = klass;
94+
const initialCount = countObjectsWithPrototype(prototype);
95+
console.log(`Initial count of ${name}: ${initialCount}`);
96+
let totalCreated = 0;
97+
for (let i = 0; i < count; ++i) {
98+
const created = await fn(i);
99+
totalCreated += created;
100+
console.log(`#${i}: created ${created} ${name}, total ${totalCreated}`);
101+
await wait(waitTime); // give GC some breathing room.
102+
const currentCount = countObjectsWithPrototype(prototype);
103+
const collected = totalCreated - (currentCount - initialCount);
104+
console.log(`#${i}: counted ${currentCount} ${name}, collected ${collected}`);
105+
if (collected > 0) {
106+
console.log(`Detected ${collected} collected ${name}, finish early`);
107+
return;
108+
}
109+
}
110+
111+
await wait(waitTime); // give GC some breathing room.
112+
const currentCount = countObjectsWithPrototype(prototype);
113+
const collected = totalCreated - (currentCount - initialCount);
114+
console.log(`Last count: counted ${currentCount} ${name}, collected ${collected}`);
115+
// Some objects with the prototype can be collected.
116+
if (collected > 0) {
117+
console.log(`Detected ${collected} collected ${name}`);
118+
return;
119+
}
120+
121+
throw new Error(`${name} cannot be collected`);
122+
}
123+
78124
module.exports = {
79125
checkIfCollectable,
80126
runAndBreathe,
127+
checkIfCollectableByCounting,
81128
};

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.