mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
v8: add v8.getCppHeapStatistics() method
Expose `CppHeap` data via `cppgc::CppHeap::CollectStatistics()` in the v8 module. PR-URL: https://github.com/nodejs/node/pull/57146 Fixes: https://github.com/nodejs/node/issues/56533 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
a914f173d2
commit
d3064e8ddb
|
|
@ -271,6 +271,92 @@ buffers and external strings.
|
|||
}
|
||||
```
|
||||
|
||||
## `v8.getCppHeapStatistics([detailLevel])`
|
||||
|
||||
Retrieves [CppHeap][] statistics regarding memory consumption and
|
||||
utilization using the V8 [`CollectStatistics()`][] function which
|
||||
may change from one V8 version to the
|
||||
next.
|
||||
|
||||
* `detailLevel` {string|undefined}: **Default:** `'detailed'`.
|
||||
Specifies the level of detail in the returned statistics.
|
||||
Accepted values are:
|
||||
* `'brief'`: Brief statistics contain only the top-level
|
||||
allocated and used
|
||||
memory statistics for the entire heap.
|
||||
* `'detailed'`: Detailed statistics also contain a break
|
||||
down per space and page, as well as freelist statistics
|
||||
and object type histograms.
|
||||
|
||||
It returns an object with a structure similar to the
|
||||
[`cppgc::HeapStatistics`][] object. See the [V8 documentation][`cppgc::HeapStatistics struct`]
|
||||
for more information about the properties of the object.
|
||||
|
||||
```js
|
||||
// Detailed
|
||||
({
|
||||
committed_size_bytes: 131072,
|
||||
resident_size_bytes: 131072,
|
||||
used_size_bytes: 152,
|
||||
space_statistics: [
|
||||
{
|
||||
name: 'NormalPageSpace0',
|
||||
committed_size_bytes: 0,
|
||||
resident_size_bytes: 0,
|
||||
used_size_bytes: 0,
|
||||
page_stats: [{}],
|
||||
free_list_stats: {},
|
||||
},
|
||||
{
|
||||
name: 'NormalPageSpace1',
|
||||
committed_size_bytes: 131072,
|
||||
resident_size_bytes: 131072,
|
||||
used_size_bytes: 152,
|
||||
page_stats: [{}],
|
||||
free_list_stats: {},
|
||||
},
|
||||
{
|
||||
name: 'NormalPageSpace2',
|
||||
committed_size_bytes: 0,
|
||||
resident_size_bytes: 0,
|
||||
used_size_bytes: 0,
|
||||
page_stats: [{}],
|
||||
free_list_stats: {},
|
||||
},
|
||||
{
|
||||
name: 'NormalPageSpace3',
|
||||
committed_size_bytes: 0,
|
||||
resident_size_bytes: 0,
|
||||
used_size_bytes: 0,
|
||||
page_stats: [{}],
|
||||
free_list_stats: {},
|
||||
},
|
||||
{
|
||||
name: 'LargePageSpace',
|
||||
committed_size_bytes: 0,
|
||||
resident_size_bytes: 0,
|
||||
used_size_bytes: 0,
|
||||
page_stats: [{}],
|
||||
free_list_stats: {},
|
||||
},
|
||||
],
|
||||
type_names: [],
|
||||
detail_level: 'detailed',
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
// Brief
|
||||
({
|
||||
committed_size_bytes: 131072,
|
||||
resident_size_bytes: 131072,
|
||||
used_size_bytes: 128864,
|
||||
space_statistics: [],
|
||||
type_names: [],
|
||||
detail_level: 'brief',
|
||||
});
|
||||
```
|
||||
|
||||
## `v8.queryObjects(ctor[, options])`
|
||||
|
||||
<!-- YAML
|
||||
|
|
@ -1343,12 +1429,14 @@ writeString('hello');
|
|||
writeString('你好');
|
||||
```
|
||||
|
||||
[CppHeap]: https://v8docs.nodesource.com/node-22.4/d9/dc4/classv8_1_1_cpp_heap.html
|
||||
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
[Hook Callbacks]: #hook-callbacks
|
||||
[V8]: https://developers.google.com/v8/
|
||||
[`--heapsnapshot-near-heap-limit`]: cli.md#--heapsnapshot-near-heap-limitmax_count
|
||||
[`AsyncLocalStorage`]: async_context.md#class-asynclocalstorage
|
||||
[`Buffer`]: buffer.md
|
||||
[`CollectStatistics()`]: https://v8docs.nodesource.com/node-22.4/d9/dc4/classv8_1_1_cpp_heap.html#a3a5d09567758e608fffde50eeabc2feb
|
||||
[`DefaultDeserializer`]: #class-v8defaultdeserializer
|
||||
[`DefaultSerializer`]: #class-v8defaultserializer
|
||||
[`Deserializer`]: #class-v8deserializer
|
||||
|
|
@ -1362,6 +1450,8 @@ writeString('你好');
|
|||
[`async_hooks`]: async_hooks.md
|
||||
[`before` callback]: #beforepromise
|
||||
[`buffer.constants.MAX_LENGTH`]: buffer.md#bufferconstantsmax_length
|
||||
[`cppgc::HeapStatistics struct`]: https://v8docs.nodesource.com/node-22.4/df/d2f/structcppgc_1_1_heap_statistics.html
|
||||
[`cppgc::HeapStatistics`]: https://v8docs.nodesource.com/node-22.4/d7/d51/heap-statistics_8h_source.html
|
||||
[`deserializer._readHostObject()`]: #deserializer_readhostobject
|
||||
[`deserializer.transferArrayBuffer()`]: #deserializertransferarraybufferid-arraybuffer
|
||||
[`init` callback]: #initpromise-parent
|
||||
|
|
|
|||
22
lib/v8.js
22
lib/v8.js
|
|
@ -37,7 +37,11 @@ const {
|
|||
} = primordials;
|
||||
|
||||
const { Buffer } = require('buffer');
|
||||
const { validateString, validateUint32 } = require('internal/validators');
|
||||
const {
|
||||
validateString,
|
||||
validateUint32,
|
||||
validateOneOf,
|
||||
} = require('internal/validators');
|
||||
const {
|
||||
Serializer,
|
||||
Deserializer,
|
||||
|
|
@ -146,6 +150,8 @@ const {
|
|||
heapStatisticsBuffer,
|
||||
heapCodeStatisticsBuffer,
|
||||
heapSpaceStatisticsBuffer,
|
||||
getCppHeapStatistics: _getCppHeapStatistics,
|
||||
detailLevel,
|
||||
} = binding;
|
||||
|
||||
const kNumberOfHeapSpaces = kHeapSpaces.length;
|
||||
|
|
@ -271,6 +277,19 @@ function setHeapSnapshotNearHeapLimit(limit) {
|
|||
_setHeapSnapshotNearHeapLimit(limit);
|
||||
}
|
||||
|
||||
const detailLevelDict = {
|
||||
__proto__: null,
|
||||
detailed: detailLevel.DETAILED,
|
||||
brief: detailLevel.BRIEF,
|
||||
};
|
||||
|
||||
function getCppHeapStatistics(type = 'detailed') {
|
||||
validateOneOf(type, 'type', ['brief', 'detailed']);
|
||||
const result = _getCppHeapStatistics(detailLevelDict[type]);
|
||||
result.detail_level = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* V8 serialization API */
|
||||
|
||||
/* JS methods for the base objects */
|
||||
|
|
@ -442,6 +461,7 @@ module.exports = {
|
|||
getHeapStatistics,
|
||||
getHeapSpaceStatistics,
|
||||
getHeapCodeStatistics,
|
||||
getCppHeapStatistics,
|
||||
setFlagsFromString,
|
||||
Serializer,
|
||||
Deserializer,
|
||||
|
|
|
|||
204
src/node_v8.cc
204
src/node_v8.cc
|
|
@ -40,9 +40,13 @@ using v8::HandleScope;
|
|||
using v8::HeapCodeStatistics;
|
||||
using v8::HeapSpaceStatistics;
|
||||
using v8::HeapStatistics;
|
||||
using v8::Int32;
|
||||
using v8::Integer;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::LocalVector;
|
||||
using v8::MaybeLocal;
|
||||
using v8::Name;
|
||||
using v8::Object;
|
||||
using v8::ScriptCompiler;
|
||||
using v8::String;
|
||||
|
|
@ -313,6 +317,191 @@ static void SetHeapStatistics(JSONWriter* writer, Isolate* isolate) {
|
|||
writer->json_arrayend();
|
||||
}
|
||||
|
||||
static MaybeLocal<Object> ConvertHeapStatsToJSObject(
|
||||
Isolate* isolate, const cppgc::HeapStatistics& stats) {
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
// Space Statistics
|
||||
LocalVector<Value> space_statistics_array(isolate);
|
||||
space_statistics_array.reserve(stats.space_stats.size());
|
||||
for (size_t i = 0; i < stats.space_stats.size(); i++) {
|
||||
const cppgc::HeapStatistics::SpaceStatistics& space_stats =
|
||||
stats.space_stats[i];
|
||||
// Page Statistics
|
||||
LocalVector<Value> page_statistics_array(isolate);
|
||||
page_statistics_array.reserve(space_stats.page_stats.size());
|
||||
for (size_t j = 0; j < space_stats.page_stats.size(); j++) {
|
||||
const cppgc::HeapStatistics::PageStatistics& page_stats =
|
||||
space_stats.page_stats[j];
|
||||
// Object Statistics
|
||||
LocalVector<Value> object_statistics_array(isolate);
|
||||
object_statistics_array.reserve(page_stats.object_statistics.size());
|
||||
for (size_t k = 0; k < page_stats.object_statistics.size(); k++) {
|
||||
const cppgc::HeapStatistics::ObjectStatsEntry& object_stats =
|
||||
page_stats.object_statistics[k];
|
||||
Local<Name> object_stats_names[] = {
|
||||
FIXED_ONE_BYTE_STRING(isolate, "allocated_bytes"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "object_count")};
|
||||
Local<Value> object_stats_values[] = {
|
||||
Uint32::NewFromUnsigned(
|
||||
isolate, static_cast<uint32_t>(object_stats.allocated_bytes)),
|
||||
Uint32::NewFromUnsigned(
|
||||
isolate, static_cast<uint32_t>(object_stats.object_count))};
|
||||
Local<Object> object_stats_object =
|
||||
Object::New(isolate,
|
||||
Null(isolate),
|
||||
object_stats_names,
|
||||
object_stats_values,
|
||||
arraysize(object_stats_names));
|
||||
object_statistics_array.emplace_back(object_stats_object);
|
||||
}
|
||||
|
||||
// Set page statistics
|
||||
Local<Name> page_stats_names[] = {
|
||||
FIXED_ONE_BYTE_STRING(isolate, "committed_size_bytes"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "resident_size_bytes"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "used_size_bytes"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "object_statistics")};
|
||||
Local<Value> page_stats_values[] = {
|
||||
Uint32::NewFromUnsigned(
|
||||
isolate, static_cast<uint32_t>(page_stats.committed_size_bytes)),
|
||||
Uint32::NewFromUnsigned(
|
||||
isolate, static_cast<uint32_t>(page_stats.resident_size_bytes)),
|
||||
Uint32::NewFromUnsigned(
|
||||
isolate, static_cast<uint32_t>(page_stats.used_size_bytes)),
|
||||
Array::New(isolate,
|
||||
object_statistics_array.data(),
|
||||
object_statistics_array.size())};
|
||||
Local<Object> page_stats_object =
|
||||
Object::New(isolate,
|
||||
Null(isolate),
|
||||
page_stats_names,
|
||||
page_stats_values,
|
||||
arraysize(page_stats_names));
|
||||
page_statistics_array.emplace_back(page_stats_object);
|
||||
}
|
||||
|
||||
// Free List Statistics
|
||||
Local<Name> free_list_statistics_names[] = {
|
||||
FIXED_ONE_BYTE_STRING(isolate, "bucket_size"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "free_count"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "free_size")};
|
||||
Local<Value> bucket_size_value;
|
||||
if (!ToV8Value(context, space_stats.free_list_stats.bucket_size)
|
||||
.ToLocal(&bucket_size_value)) {
|
||||
return MaybeLocal<Object>();
|
||||
}
|
||||
Local<Value> free_count_value;
|
||||
if (!ToV8Value(context, space_stats.free_list_stats.free_count)
|
||||
.ToLocal(&free_count_value)) {
|
||||
return MaybeLocal<Object>();
|
||||
}
|
||||
Local<Value> free_size_value;
|
||||
if (!ToV8Value(context, space_stats.free_list_stats.free_size)
|
||||
.ToLocal(&free_size_value)) {
|
||||
return MaybeLocal<Object>();
|
||||
}
|
||||
Local<Value> free_list_statistics_values[] = {
|
||||
bucket_size_value, free_count_value, free_size_value};
|
||||
|
||||
Local<Object> free_list_statistics_obj =
|
||||
Object::New(isolate,
|
||||
Null(isolate),
|
||||
free_list_statistics_names,
|
||||
free_list_statistics_values,
|
||||
arraysize(free_list_statistics_names));
|
||||
|
||||
// Set Space Statistics
|
||||
Local<Name> space_stats_names[] = {
|
||||
FIXED_ONE_BYTE_STRING(isolate, "name"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "committed_size_bytes"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "resident_size_bytes"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "used_size_bytes"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "page_stats"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "free_list_stats")};
|
||||
|
||||
Local<Value> name_value;
|
||||
if (!ToV8Value(context, stats.space_stats[i].name, isolate)
|
||||
.ToLocal(&name_value)) {
|
||||
return MaybeLocal<Object>();
|
||||
}
|
||||
Local<Value> space_stats_values[] = {
|
||||
name_value,
|
||||
Uint32::NewFromUnsigned(
|
||||
isolate,
|
||||
static_cast<uint32_t>(stats.space_stats[i].committed_size_bytes)),
|
||||
Uint32::NewFromUnsigned(
|
||||
isolate,
|
||||
static_cast<uint32_t>(stats.space_stats[i].resident_size_bytes)),
|
||||
Uint32::NewFromUnsigned(
|
||||
isolate,
|
||||
static_cast<uint32_t>(stats.space_stats[i].used_size_bytes)),
|
||||
Array::New(isolate,
|
||||
page_statistics_array.data(),
|
||||
page_statistics_array.size()),
|
||||
free_list_statistics_obj,
|
||||
};
|
||||
Local<Object> space_stats_object =
|
||||
Object::New(isolate,
|
||||
Null(isolate),
|
||||
space_stats_names,
|
||||
space_stats_values,
|
||||
arraysize(space_stats_names));
|
||||
space_statistics_array.emplace_back(space_stats_object);
|
||||
}
|
||||
|
||||
// Set heap statistics
|
||||
Local<Name> heap_statistics_names[] = {
|
||||
FIXED_ONE_BYTE_STRING(isolate, "committed_size_bytes"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "resident_size_bytes"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "used_size_bytes"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "space_statistics"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "type_names")};
|
||||
|
||||
Local<Value> type_names_value;
|
||||
if (!ToV8Value(context, stats.type_names, isolate)
|
||||
.ToLocal(&type_names_value)) {
|
||||
return MaybeLocal<Object>();
|
||||
}
|
||||
Local<Value> heap_statistics_values[] = {
|
||||
Uint32::NewFromUnsigned(
|
||||
isolate, static_cast<uint32_t>(stats.committed_size_bytes)),
|
||||
Uint32::NewFromUnsigned(isolate,
|
||||
static_cast<uint32_t>(stats.resident_size_bytes)),
|
||||
Uint32::NewFromUnsigned(isolate,
|
||||
static_cast<uint32_t>(stats.used_size_bytes)),
|
||||
Array::New(isolate,
|
||||
space_statistics_array.data(),
|
||||
space_statistics_array.size()),
|
||||
type_names_value};
|
||||
|
||||
Local<Object> heap_statistics_object =
|
||||
Object::New(isolate,
|
||||
Null(isolate),
|
||||
heap_statistics_names,
|
||||
heap_statistics_values,
|
||||
arraysize(heap_statistics_names));
|
||||
|
||||
return heap_statistics_object;
|
||||
}
|
||||
|
||||
static void GetCppHeapStatistics(const FunctionCallbackInfo<Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
HandleScope handle_scope(isolate);
|
||||
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsInt32());
|
||||
|
||||
cppgc::HeapStatistics stats = isolate->GetCppHeap()->CollectStatistics(
|
||||
static_cast<cppgc::HeapStatistics::DetailLevel>(
|
||||
args[0].As<Int32>()->Value()));
|
||||
|
||||
Local<Object> result;
|
||||
if (!ConvertHeapStatsToJSObject(isolate, stats).ToLocal(&result)) {
|
||||
return;
|
||||
}
|
||||
args.GetReturnValue().Set(result);
|
||||
}
|
||||
|
||||
static void BeforeGCCallback(Isolate* isolate,
|
||||
v8::GCType gc_type,
|
||||
v8::GCCallbackFlags flags,
|
||||
|
|
@ -457,6 +646,8 @@ void Initialize(Local<Object> target,
|
|||
target,
|
||||
"updateHeapCodeStatisticsBuffer",
|
||||
UpdateHeapCodeStatisticsBuffer);
|
||||
SetMethodNoSideEffect(
|
||||
context, target, "getCppHeapStatistics", GetCppHeapStatistics);
|
||||
|
||||
size_t number_of_heap_spaces = env->isolate()->NumberOfHeapSpaces();
|
||||
|
||||
|
|
@ -510,6 +701,18 @@ void Initialize(Local<Object> target,
|
|||
SetProtoMethod(env->isolate(), t, "start", GCProfiler::Start);
|
||||
SetProtoMethod(env->isolate(), t, "stop", GCProfiler::Stop);
|
||||
SetConstructorFunction(context, target, "GCProfiler", t);
|
||||
|
||||
{
|
||||
Isolate* isolate = env->isolate();
|
||||
Local<Object> detail_level = Object::New(isolate);
|
||||
cppgc::HeapStatistics::DetailLevel DETAILED =
|
||||
cppgc::HeapStatistics::DetailLevel::kDetailed;
|
||||
cppgc::HeapStatistics::DetailLevel BRIEF =
|
||||
cppgc::HeapStatistics::DetailLevel::kBrief;
|
||||
NODE_DEFINE_CONSTANT(detail_level, DETAILED);
|
||||
NODE_DEFINE_CONSTANT(detail_level, BRIEF);
|
||||
READONLY_PROPERTY(target, "detailLevel", detail_level);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||
|
|
@ -522,6 +725,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
|||
registry->Register(GCProfiler::New);
|
||||
registry->Register(GCProfiler::Start);
|
||||
registry->Register(GCProfiler::Stop);
|
||||
registry->Register(GetCppHeapStatistics);
|
||||
registry->Register(IsStringOneByteRepresentation);
|
||||
registry->Register(FastIsStringOneByteRepresentation);
|
||||
registry->Register(fast_is_string_one_byte_representation_.GetTypeInfo());
|
||||
|
|
|
|||
125
test/parallel/test-cppheap-stats.js
Normal file
125
test/parallel/test-cppheap-stats.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
'use strict';
|
||||
// Tests that v8.getCppHeapStatistics() returns an object with an expected shape.
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const v8 = require('v8');
|
||||
|
||||
// Detailed heap statistics
|
||||
const heapStatsDetailed = v8.getCppHeapStatistics('detailed');
|
||||
assert.strictEqual(heapStatsDetailed.detail_level, 'detailed');
|
||||
const expectedTopLevelKeys = [
|
||||
'committed_size_bytes',
|
||||
'resident_size_bytes',
|
||||
'used_size_bytes',
|
||||
'detail_level',
|
||||
'space_statistics',
|
||||
'type_names',
|
||||
].sort();
|
||||
|
||||
// Check top level properties
|
||||
const actualTopLevelKeys = Object.keys(heapStatsDetailed).sort();
|
||||
assert.deepStrictEqual(actualTopLevelKeys, expectedTopLevelKeys);
|
||||
|
||||
// Check types of top level properties
|
||||
assert.strictEqual(typeof heapStatsDetailed.committed_size_bytes, 'number');
|
||||
assert.strictEqual(typeof heapStatsDetailed.resident_size_bytes, 'number');
|
||||
assert.strictEqual(typeof heapStatsDetailed.used_size_bytes, 'number');
|
||||
assert.strictEqual(typeof heapStatsDetailed.detail_level, 'string');
|
||||
assert.strictEqual(Array.isArray(heapStatsDetailed.space_statistics), true);
|
||||
assert.strictEqual(Array.isArray(heapStatsDetailed.type_names), true);
|
||||
|
||||
|
||||
// Check space statistics array
|
||||
const expectedSpaceKeys = [
|
||||
'name',
|
||||
'committed_size_bytes',
|
||||
'resident_size_bytes',
|
||||
'used_size_bytes',
|
||||
'page_stats',
|
||||
'free_list_stats',
|
||||
].sort();
|
||||
|
||||
heapStatsDetailed.space_statistics.forEach((space) => {
|
||||
const actualSpaceKeys = Object.keys(space).sort();
|
||||
assert.deepStrictEqual(actualSpaceKeys, expectedSpaceKeys);
|
||||
assert.strictEqual(typeof space.name, 'string');
|
||||
assert.strictEqual(typeof space.committed_size_bytes, 'number');
|
||||
assert.strictEqual(typeof space.resident_size_bytes, 'number');
|
||||
assert.strictEqual(typeof space.used_size_bytes, 'number');
|
||||
assert.strictEqual(Array.isArray(space.page_stats), true);
|
||||
assert.strictEqual(typeof space.free_list_stats, 'object');
|
||||
});
|
||||
|
||||
// Check page statistics array
|
||||
const expectedPageKeys = [
|
||||
'committed_size_bytes',
|
||||
'resident_size_bytes',
|
||||
'used_size_bytes',
|
||||
'object_statistics',
|
||||
].sort();
|
||||
|
||||
heapStatsDetailed.space_statistics.forEach((space) => {
|
||||
space.page_stats.forEach((page) => {
|
||||
const actualPageKeys = Object.keys(page).sort();
|
||||
assert.deepStrictEqual(actualPageKeys, expectedPageKeys);
|
||||
assert.strictEqual(typeof page.committed_size_bytes, 'number');
|
||||
assert.strictEqual(typeof page.resident_size_bytes, 'number');
|
||||
assert.strictEqual(typeof page.used_size_bytes, 'number');
|
||||
assert.strictEqual(Array.isArray(page.object_statistics), true);
|
||||
});
|
||||
});
|
||||
|
||||
// Check free list statistics
|
||||
const expectedFreeListKeys = ['bucket_size', 'free_count', 'free_size'].sort();
|
||||
|
||||
heapStatsDetailed.space_statistics.forEach((space) => {
|
||||
const actualFreeListKeys = Object.keys(space.free_list_stats).sort();
|
||||
assert.deepStrictEqual(actualFreeListKeys, expectedFreeListKeys);
|
||||
assert.strictEqual(Array.isArray(space.free_list_stats.bucket_size), true);
|
||||
assert.strictEqual(Array.isArray(space.free_list_stats.free_count), true);
|
||||
assert.strictEqual(Array.isArray(space.free_list_stats.free_size), true);
|
||||
});
|
||||
|
||||
// Check object statistics
|
||||
const expectedObjectStatsKeys = ['allocated_bytes', 'object_count'].sort();
|
||||
|
||||
heapStatsDetailed.space_statistics.forEach((space) => {
|
||||
space.page_stats.forEach((page) => {
|
||||
page.object_statistics.forEach((objectStats) => {
|
||||
const actualObjectStatsKeys = Object.keys(objectStats).sort();
|
||||
assert.deepStrictEqual(actualObjectStatsKeys, expectedObjectStatsKeys);
|
||||
assert.strictEqual(typeof objectStats.allocated_bytes, 'number');
|
||||
assert.strictEqual(typeof objectStats.object_count, 'number');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Check type names
|
||||
heapStatsDetailed.type_names.forEach((typeName) => {
|
||||
assert.strictEqual(typeof typeName, 'string');
|
||||
});
|
||||
|
||||
// Brief heap statistics
|
||||
const heapStatsBrief = v8.getCppHeapStatistics('brief');
|
||||
const expectedBriefKeys = [
|
||||
'committed_size_bytes',
|
||||
'resident_size_bytes',
|
||||
'used_size_bytes',
|
||||
'detail_level',
|
||||
'space_statistics',
|
||||
'type_names',
|
||||
].sort();
|
||||
|
||||
// Check top level properties
|
||||
const actualBriefKeys = Object.keys(heapStatsBrief).sort();
|
||||
assert.strictEqual(heapStatsBrief.detail_level, 'brief');
|
||||
assert.deepStrictEqual(actualBriefKeys, expectedBriefKeys);
|
||||
|
||||
// Check types of top level properties
|
||||
assert.strictEqual(typeof heapStatsBrief.committed_size_bytes, 'number');
|
||||
assert.strictEqual(typeof heapStatsBrief.resident_size_bytes, 'number');
|
||||
assert.strictEqual(typeof heapStatsBrief.used_size_bytes, 'number');
|
||||
assert.strictEqual(typeof heapStatsBrief.detail_level, 'string');
|
||||
assert.strictEqual(Array.isArray(heapStatsBrief.space_statistics), true);
|
||||
assert.strictEqual(Array.isArray(heapStatsBrief.type_names), true);
|
||||
Loading…
Reference in New Issue
Block a user