v8,src: expose statistics about heap spaces · nodejs/node@30d60cf · GitHub
Skip to content

Commit 30d60cf

Browse files
bripkensMylesBorins
authored andcommitted
v8,src: expose statistics about heap spaces
Provide means to inspect information about the separate heap spaces via a callable API. This is helpful to analyze memory issues. Fixes: #2079 PR-URL: #4463 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 4e07bd4 commit 30d60cf

6 files changed

Lines changed: 202 additions & 2 deletions

File tree

doc/api/v8.md

Lines changed: 49 additions & 0 deletions

lib/v8.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,28 @@
1616

1717
const v8binding = process.binding('v8');
1818

19+
// Properties for heap statistics buffer extraction.
1920
const heapStatisticsBuffer =
2021
new Uint32Array(v8binding.heapStatisticsArrayBuffer);
21-
2222
const kTotalHeapSizeIndex = v8binding.kTotalHeapSizeIndex;
2323
const kTotalHeapSizeExecutableIndex = v8binding.kTotalHeapSizeExecutableIndex;
2424
const kTotalPhysicalSizeIndex = v8binding.kTotalPhysicalSizeIndex;
2525
const kTotalAvailableSize = v8binding.kTotalAvailableSize;
2626
const kUsedHeapSizeIndex = v8binding.kUsedHeapSizeIndex;
2727
const kHeapSizeLimitIndex = v8binding.kHeapSizeLimitIndex;
2828

29+
// Properties for heap space statistics buffer extraction.
30+
const heapSpaceStatisticsBuffer =
31+
new Uint32Array(v8binding.heapSpaceStatisticsArrayBuffer);
32+
const kHeapSpaces = v8binding.kHeapSpaces;
33+
const kNumberOfHeapSpaces = kHeapSpaces.length;
34+
const kHeapSpaceStatisticsPropertiesCount =
35+
v8binding.kHeapSpaceStatisticsPropertiesCount;
36+
const kSpaceSizeIndex = v8binding.kSpaceSizeIndex;
37+
const kSpaceUsedSizeIndex = v8binding.kSpaceUsedSizeIndex;
38+
const kSpaceAvailableSizeIndex = v8binding.kSpaceAvailableSizeIndex;
39+
const kPhysicalSpaceSizeIndex = v8binding.kPhysicalSpaceSizeIndex;
40+
2941
exports.getHeapStatistics = function() {
3042
const buffer = heapStatisticsBuffer;
3143

@@ -42,3 +54,22 @@ exports.getHeapStatistics = function() {
4254
};
4355

4456
exports.setFlagsFromString = v8binding.setFlagsFromString;
57+
58+
exports.getHeapSpaceStatistics = function() {
59+
const heapSpaceStatistics = new Array(kNumberOfHeapSpaces);
60+
const buffer = heapSpaceStatisticsBuffer;
61+
v8binding.updateHeapSpaceStatisticsArrayBuffer();
62+
63+
for (var i = 0; i < kNumberOfHeapSpaces; i++) {
64+
const propertyOffset = i * kHeapSpaceStatisticsPropertiesCount;
65+
heapSpaceStatistics[i] = {
66+
space_name: kHeapSpaces[i],
67+
space_size: buffer[propertyOffset + kSpaceSizeIndex],
68+
space_used_size: buffer[propertyOffset + kSpaceUsedSizeIndex],
69+
space_available_size: buffer[propertyOffset + kSpaceAvailableSizeIndex],
70+
physical_space_size: buffer[propertyOffset + kPhysicalSpaceSizeIndex]
71+
};
72+
}
73+
74+
return heapSpaceStatistics;
75+
};

src/env-inl.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ inline Environment::~Environment() {
242242
isolate_data()->Put();
243243

244244
delete[] heap_statistics_buffer_;
245+
delete[] heap_space_statistics_buffer_;
245246
delete[] http_parser_buffer_;
246247
}
247248

@@ -387,6 +388,17 @@ inline void Environment::set_heap_statistics_buffer(uint32_t* pointer) {
387388
heap_statistics_buffer_ = pointer;
388389
}
389390

391+
inline uint32_t* Environment::heap_space_statistics_buffer() const {
392+
CHECK_NE(heap_space_statistics_buffer_, nullptr);
393+
return heap_space_statistics_buffer_;
394+
}
395+
396+
inline void Environment::set_heap_space_statistics_buffer(uint32_t* pointer) {
397+
CHECK_EQ(heap_space_statistics_buffer_, nullptr); // Should be set only once.
398+
heap_space_statistics_buffer_ = pointer;
399+
}
400+
401+
390402
inline char* Environment::http_parser_buffer() const {
391403
return http_parser_buffer_;
392404
}

src/env.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,9 @@ class Environment {
460460
inline uint32_t* heap_statistics_buffer() const;
461461
inline void set_heap_statistics_buffer(uint32_t* pointer);
462462

463+
inline uint32_t* heap_space_statistics_buffer() const;
464+
inline void set_heap_space_statistics_buffer(uint32_t* pointer);
465+
463466
inline char* http_parser_buffer() const;
464467
inline void set_http_parser_buffer(char* buffer);
465468

@@ -563,6 +566,7 @@ class Environment {
563566
int handle_cleanup_waiting_;
564567

565568
uint32_t* heap_statistics_buffer_ = nullptr;
569+
uint32_t* heap_space_statistics_buffer_ = nullptr;
566570

567571
char* http_parser_buffer_;
568572

src/node_v8.cc

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77

88
namespace node {
99

10+
using v8::Array;
1011
using v8::ArrayBuffer;
1112
using v8::Context;
1213
using v8::Function;
1314
using v8::FunctionCallbackInfo;
15+
using v8::HeapSpaceStatistics;
1416
using v8::HeapStatistics;
1517
using v8::Isolate;
1618
using v8::Local;
19+
using v8::NewStringType;
1720
using v8::Object;
1821
using v8::String;
1922
using v8::Uint32;
@@ -34,6 +37,21 @@ static const size_t kHeapStatisticsPropertiesCount =
3437
HEAP_STATISTICS_PROPERTIES(V);
3538
#undef V
3639

40+
#define HEAP_SPACE_STATISTICS_PROPERTIES(V) \
41+
V(0, space_size, kSpaceSizeIndex) \
42+
V(1, space_used_size, kSpaceUsedSizeIndex) \
43+
V(2, space_available_size, kSpaceAvailableSizeIndex) \
44+
V(3, physical_space_size, kPhysicalSpaceSizeIndex)
45+
46+
#define V(a, b, c) +1
47+
static const size_t kHeapSpaceStatisticsPropertiesCount =
48+
HEAP_SPACE_STATISTICS_PROPERTIES(V);
49+
#undef V
50+
51+
// Will be populated in InitializeV8Bindings.
52+
static size_t number_of_heap_spaces = 0;
53+
54+
3755
void UpdateHeapStatisticsArrayBuffer(const FunctionCallbackInfo<Value>& args) {
3856
Environment* env = Environment::GetCurrent(args);
3957
HeapStatistics s;
@@ -45,6 +63,23 @@ void UpdateHeapStatisticsArrayBuffer(const FunctionCallbackInfo<Value>& args) {
4563
}
4664

4765

66+
void UpdateHeapSpaceStatisticsBuffer(const FunctionCallbackInfo<Value>& args) {
67+
Environment* env = Environment::GetCurrent(args);
68+
HeapSpaceStatistics s;
69+
Isolate* const isolate = env->isolate();
70+
uint32_t* buffer = env->heap_space_statistics_buffer();
71+
72+
for (size_t i = 0; i < number_of_heap_spaces; i++) {
73+
isolate->GetHeapSpaceStatistics(&s, i);
74+
size_t const property_offset = i * kHeapSpaceStatisticsPropertiesCount;
75+
#define V(index, name, _) buffer[property_offset + index] = \
76+
static_cast<uint32_t>(s.name());
77+
HEAP_SPACE_STATISTICS_PROPERTIES(V)
78+
#undef V
79+
}
80+
}
81+
82+
4883
void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
4984
Environment* env = Environment::GetCurrent(args);
5085

@@ -62,10 +97,10 @@ void InitializeV8Bindings(Local<Object> target,
6297
Local<Value> unused,
6398
Local<Context> context) {
6499
Environment* env = Environment::GetCurrent(context);
100+
65101
env->SetMethod(target,
66102
"updateHeapStatisticsArrayBuffer",
67103
UpdateHeapStatisticsArrayBuffer);
68-
env->SetMethod(target, "setFlagsFromString", SetFlagsFromString);
69104

70105
env->set_heap_statistics_buffer(new uint32_t[kHeapStatisticsPropertiesCount]);
71106

@@ -84,6 +119,56 @@ void InitializeV8Bindings(Local<Object> target,
84119

85120
HEAP_STATISTICS_PROPERTIES(V)
86121
#undef V
122+
123+
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(),
124+
"kHeapSpaceStatisticsPropertiesCount"),
125+
Uint32::NewFromUnsigned(env->isolate(),
126+
kHeapSpaceStatisticsPropertiesCount));
127+
128+
number_of_heap_spaces = env->isolate()->NumberOfHeapSpaces();
129+
130+
// Heap space names are extracted once and exposed to JavaScript to
131+
// avoid excessive creation of heap space name Strings.
132+
HeapSpaceStatistics s;
133+
const Local<Array> heap_spaces = Array::New(env->isolate(),
134+
number_of_heap_spaces);
135+
for (size_t i = 0; i < number_of_heap_spaces; i++) {
136+
env->isolate()->GetHeapSpaceStatistics(&s, i);
137+
Local<String> heap_space_name = String::NewFromUtf8(env->isolate(),
138+
s.space_name(),
139+
NewStringType::kNormal)
140+
.ToLocalChecked();
141+
heap_spaces->Set(i, heap_space_name);
142+
}
143+
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kHeapSpaces"),
144+
heap_spaces);
145+
146+
env->SetMethod(target,
147+
"updateHeapSpaceStatisticsArrayBuffer",
148+
UpdateHeapSpaceStatisticsBuffer);
149+
150+
env->set_heap_space_statistics_buffer(
151+
new uint32_t[kHeapSpaceStatisticsPropertiesCount * number_of_heap_spaces]);
152+
153+
const size_t heap_space_statistics_buffer_byte_length =
154+
sizeof(*env->heap_space_statistics_buffer()) *
155+
kHeapSpaceStatisticsPropertiesCount *
156+
number_of_heap_spaces;
157+
158+
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(),
159+
"heapSpaceStatisticsArrayBuffer"),
160+
ArrayBuffer::New(env->isolate(),
161+
env->heap_space_statistics_buffer(),
162+
heap_space_statistics_buffer_byte_length));
163+
164+
#define V(i, _, name) \
165+
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), #name), \
166+
Uint32::NewFromUnsigned(env->isolate(), i));
167+
168+
HEAP_SPACE_STATISTICS_PROPERTIES(V)
169+
#undef V
170+
171+
env->SetMethod(target, "setFlagsFromString", SetFlagsFromString);
87172
}
88173

89174
} // namespace node

test/parallel/test-v8-stats.js

Lines changed: 19 additions & 0 deletions

0 commit comments

Comments
 (0)