Skip to content

Commit

Permalink
src: expose cppgc::CppHeap::CollectStatistics()
Browse files Browse the repository at this point in the history
Expose `CppHeap` data via
`cppgc::CppHeap::CollectStatistics()` in the v8 module.
  • Loading branch information
Aditi-1400 committed Feb 20, 2025
1 parent cbedcd1 commit 0f5a16c
Show file tree
Hide file tree
Showing 4 changed files with 393 additions and 0 deletions.
75 changes: 75 additions & 0 deletions doc/api/v8.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,78 @@ buffers and external strings.
}
```

## `v8.getCppHeapStatistics(detailLevel)`

This method returns [CppHeap][] statistics regarding memory consumption and
utilization. See V8 [`CollectStatistics()`][] API. It returns a
[`HeapStatistics`][] object. You can learn more about the properties in
the [V8 documentation](https://v8docs.nodesource.com/node-22.4/df/d2f/structcppgc_1_1_heap_statistics.html).

```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
Expand Down Expand Up @@ -1304,19 +1376,22 @@ setTimeout(() => {
}, 1000);
```

[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
[`ERR_BUFFER_TOO_LARGE`]: errors.md#err_buffer_too_large
[`Error`]: errors.md#class-error
[`GetHeapCodeAndMetadataStatistics`]: https://v8docs.nodesource.com/node-13.2/d5/dda/classv8_1_1_isolate.html#a6079122af17612ef54ef3348ce170866
[`GetHeapSpaceStatistics`]: https://v8docs.nodesource.com/node-13.2/d5/dda/classv8_1_1_isolate.html#ac673576f24fdc7a33378f8f57e1d13a4
[`HeapStatistics`]: https://v8docs.nodesource.com/node-22.4/d7/d51/heap-statistics_8h_source.html
[`NODE_V8_COVERAGE`]: cli.md#node_v8_coveragedir
[`Serializer`]: #class-v8serializer
[`after` callback]: #afterpromise
Expand Down
19 changes: 19 additions & 0 deletions lib/v8.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const {
const promiseHooks = require('internal/promise_hooks');
const { getOptionValue } = require('internal/options');

const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes;
/**
* Generates a snapshot of the current V8 heap
* and writes it to a JSON file.
Expand Down Expand Up @@ -145,6 +146,8 @@ const {
heapStatisticsBuffer,
heapCodeStatisticsBuffer,
heapSpaceStatisticsBuffer,
getCppHeapStatistics: _getCppHeapStatistics,
detailLevel,
} = binding;

const kNumberOfHeapSpaces = kHeapSpaces.length;
Expand Down Expand Up @@ -259,6 +262,21 @@ function setHeapSnapshotNearHeapLimit(limit) {
_setHeapSnapshotNearHeapLimit(limit);
}

const detailLevelDict = {
__proto__: null,
detailed: detailLevel.DETAILED,
brief: detailLevel.BRIEF,
};

function getCppHeapStatistics(type = 'detailed') {
if (type !== undefined && type !== 'detailed' && type !== 'brief') {
throw new ERR_INVALID_ARG_TYPE('Detail type', 'brief, detailed or undefined', type);
}
const result = _getCppHeapStatistics(detailLevelDict[type]);
result.detail_level = type;
return result;
}

/* V8 serialization API */

/* JS methods for the base objects */
Expand Down Expand Up @@ -430,6 +448,7 @@ module.exports = {
getHeapStatistics,
getHeapSpaceStatistics,
getHeapCodeStatistics,
getCppHeapStatistics,
setFlagsFromString,
Serializer,
Deserializer,
Expand Down
175 changes: 175 additions & 0 deletions src/node_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ using v8::HeapStatistics;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::LocalVector;
using v8::Name;
using v8::Object;
using v8::ScriptCompiler;
using v8::String;
Expand Down Expand Up @@ -295,6 +297,164 @@ static void SetHeapStatistics(JSONWriter* writer, Isolate* isolate) {
writer->json_arrayend();
}

static Local<Object> ConvertHeapStatsToJSObject(
Isolate* isolate, const cppgc::HeapStatistics& stats) {
Local<Context> context = isolate->GetCurrentContext();
// Space Statistics
LocalVector<Value> space_statistics_array(isolate);
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);
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);
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> free_list_statistics_values[] = {
ToV8Value(context, space_stats.free_list_stats.bucket_size, isolate)
.ToLocalChecked(),
ToV8Value(context, space_stats.free_list_stats.free_count, isolate)
.ToLocalChecked(),
ToV8Value(context, space_stats.free_list_stats.free_size, isolate)
.ToLocalChecked()};
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> space_stats_values[] = {
ToV8Value(context, stats.space_stats[i].name.c_str(), isolate)
.ToLocalChecked(),
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> 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()),
ToV8Value(context, stats.type_names, isolate).ToLocalChecked()};

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<v8::Int32>()->Value()));

Local<Object> result = ConvertHeapStatsToJSObject(isolate, stats);
args.GetReturnValue().Set(result);
}

static void BeforeGCCallback(Isolate* isolate,
v8::GCType gc_type,
v8::GCCallbackFlags flags,
Expand Down Expand Up @@ -440,6 +600,8 @@ void Initialize(Local<Object> target,
target,
"updateHeapCodeStatisticsBuffer",
UpdateHeapCodeStatisticsBuffer);
SetMethod(
context, target, "getCppHeapStatistics", getCppHeapStatistics);

size_t number_of_heap_spaces = env->isolate()->NumberOfHeapSpaces();

Expand Down Expand Up @@ -486,6 +648,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) {
Expand All @@ -498,6 +672,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(GCProfiler::New);
registry->Register(GCProfiler::Start);
registry->Register(GCProfiler::Stop);
registry->Register(getCppHeapStatistics);
}

} // namespace v8_utils
Expand Down
Loading

0 comments on commit 0f5a16c

Please sign in to comment.