Skip to content

Commit

Permalink
v8,src: expose statistics about heap spaces
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
bripkens authored and MylesBorins committed Jan 13, 2017
1 parent 20bee0f commit 504b01b
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 2 deletions.
49 changes: 49 additions & 0 deletions doc/api/v8.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,55 @@ Returns an object with the following properties
}
```

## getHeapSpaceStatistics()

Returns statistics about the V8 heap spaces, i.e. the segments which make up
the V8 heap. Order of heap spaces nor availability of a heap space can be
guaranteed as the statistics are provided via the V8 `GetHeapSpaceStatistics`
function.

Example result:

```
[
{
"space_name": "new_space",
"space_size": 2063872,
"space_used_size": 951112,
"space_available_size": 80824,
"physical_space_size": 2063872
},
{
"space_name": "old_space",
"space_size": 3090560,
"space_used_size": 2493792,
"space_available_size": 0,
"physical_space_size": 3090560
},
{
"space_name": "code_space",
"space_size": 1260160,
"space_used_size": 644256,
"space_available_size": 960,
"physical_space_size": 1260160
},
{
"space_name": "map_space",
"space_size": 1094160,
"space_used_size": 201608,
"space_available_size": 0,
"physical_space_size": 1094160
},
{
"space_name": "large_object_space",
"space_size": 0,
"space_used_size": 0,
"space_available_size": 1490980608,
"physical_space_size": 0
}
]
```

## setFlagsFromString(string)
<!-- YAML
added: v1.0.0
Expand Down
33 changes: 32 additions & 1 deletion lib/v8.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,28 @@

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

// Properties for heap statistics buffer extraction.
const heapStatisticsBuffer =
new Uint32Array(v8binding.heapStatisticsArrayBuffer);

const kTotalHeapSizeIndex = v8binding.kTotalHeapSizeIndex;
const kTotalHeapSizeExecutableIndex = v8binding.kTotalHeapSizeExecutableIndex;
const kTotalPhysicalSizeIndex = v8binding.kTotalPhysicalSizeIndex;
const kTotalAvailableSize = v8binding.kTotalAvailableSize;
const kUsedHeapSizeIndex = v8binding.kUsedHeapSizeIndex;
const kHeapSizeLimitIndex = v8binding.kHeapSizeLimitIndex;

// Properties for heap space statistics buffer extraction.
const heapSpaceStatisticsBuffer =
new Uint32Array(v8binding.heapSpaceStatisticsArrayBuffer);
const kHeapSpaces = v8binding.kHeapSpaces;
const kNumberOfHeapSpaces = kHeapSpaces.length;
const kHeapSpaceStatisticsPropertiesCount =
v8binding.kHeapSpaceStatisticsPropertiesCount;
const kSpaceSizeIndex = v8binding.kSpaceSizeIndex;
const kSpaceUsedSizeIndex = v8binding.kSpaceUsedSizeIndex;
const kSpaceAvailableSizeIndex = v8binding.kSpaceAvailableSizeIndex;
const kPhysicalSpaceSizeIndex = v8binding.kPhysicalSpaceSizeIndex;

exports.getHeapStatistics = function() {
const buffer = heapStatisticsBuffer;

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

exports.setFlagsFromString = v8binding.setFlagsFromString;

exports.getHeapSpaceStatistics = function() {
const heapSpaceStatistics = new Array(kNumberOfHeapSpaces);
const buffer = heapSpaceStatisticsBuffer;
v8binding.updateHeapSpaceStatisticsArrayBuffer();

for (var i = 0; i < kNumberOfHeapSpaces; i++) {
const propertyOffset = i * kHeapSpaceStatisticsPropertiesCount;
heapSpaceStatistics[i] = {
space_name: kHeapSpaces[i],
space_size: buffer[propertyOffset + kSpaceSizeIndex],
space_used_size: buffer[propertyOffset + kSpaceUsedSizeIndex],
space_available_size: buffer[propertyOffset + kSpaceAvailableSizeIndex],
physical_space_size: buffer[propertyOffset + kPhysicalSpaceSizeIndex]
};
}

return heapSpaceStatistics;
};
12 changes: 12 additions & 0 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ inline Environment::~Environment() {
isolate_data()->Put();

delete[] heap_statistics_buffer_;
delete[] heap_space_statistics_buffer_;
delete[] http_parser_buffer_;
}

Expand Down Expand Up @@ -387,6 +388,17 @@ inline void Environment::set_heap_statistics_buffer(uint32_t* pointer) {
heap_statistics_buffer_ = pointer;
}

inline uint32_t* Environment::heap_space_statistics_buffer() const {
CHECK_NE(heap_space_statistics_buffer_, nullptr);
return heap_space_statistics_buffer_;
}

inline void Environment::set_heap_space_statistics_buffer(uint32_t* pointer) {
CHECK_EQ(heap_space_statistics_buffer_, nullptr); // Should be set only once.
heap_space_statistics_buffer_ = pointer;
}


inline char* Environment::http_parser_buffer() const {
return http_parser_buffer_;
}
Expand Down
4 changes: 4 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ class Environment {
inline uint32_t* heap_statistics_buffer() const;
inline void set_heap_statistics_buffer(uint32_t* pointer);

inline uint32_t* heap_space_statistics_buffer() const;
inline void set_heap_space_statistics_buffer(uint32_t* pointer);

inline char* http_parser_buffer() const;
inline void set_http_parser_buffer(char* buffer);

Expand Down Expand Up @@ -563,6 +566,7 @@ class Environment {
int handle_cleanup_waiting_;

uint32_t* heap_statistics_buffer_ = nullptr;
uint32_t* heap_space_statistics_buffer_ = nullptr;

char* http_parser_buffer_;

Expand Down
87 changes: 86 additions & 1 deletion src/node_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@

namespace node {

using v8::Array;
using v8::ArrayBuffer;
using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::HeapSpaceStatistics;
using v8::HeapStatistics;
using v8::Isolate;
using v8::Local;
using v8::NewStringType;
using v8::Object;
using v8::String;
using v8::Uint32;
Expand All @@ -34,6 +37,21 @@ static const size_t kHeapStatisticsPropertiesCount =
HEAP_STATISTICS_PROPERTIES(V);
#undef V

#define HEAP_SPACE_STATISTICS_PROPERTIES(V) \
V(0, space_size, kSpaceSizeIndex) \
V(1, space_used_size, kSpaceUsedSizeIndex) \
V(2, space_available_size, kSpaceAvailableSizeIndex) \
V(3, physical_space_size, kPhysicalSpaceSizeIndex)

#define V(a, b, c) +1
static const size_t kHeapSpaceStatisticsPropertiesCount =
HEAP_SPACE_STATISTICS_PROPERTIES(V);
#undef V

// Will be populated in InitializeV8Bindings.
static size_t number_of_heap_spaces = 0;


void UpdateHeapStatisticsArrayBuffer(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
HeapStatistics s;
Expand All @@ -45,6 +63,23 @@ void UpdateHeapStatisticsArrayBuffer(const FunctionCallbackInfo<Value>& args) {
}


void UpdateHeapSpaceStatisticsBuffer(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
HeapSpaceStatistics s;
Isolate* const isolate = env->isolate();
uint32_t* buffer = env->heap_space_statistics_buffer();

for (size_t i = 0; i < number_of_heap_spaces; i++) {
isolate->GetHeapSpaceStatistics(&s, i);
size_t const property_offset = i * kHeapSpaceStatisticsPropertiesCount;
#define V(index, name, _) buffer[property_offset + index] = \
static_cast<uint32_t>(s.name());
HEAP_SPACE_STATISTICS_PROPERTIES(V)
#undef V
}
}


void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Expand All @@ -62,10 +97,10 @@ void InitializeV8Bindings(Local<Object> target,
Local<Value> unused,
Local<Context> context) {
Environment* env = Environment::GetCurrent(context);

env->SetMethod(target,
"updateHeapStatisticsArrayBuffer",
UpdateHeapStatisticsArrayBuffer);
env->SetMethod(target, "setFlagsFromString", SetFlagsFromString);

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

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

HEAP_STATISTICS_PROPERTIES(V)
#undef V

target->Set(FIXED_ONE_BYTE_STRING(env->isolate(),
"kHeapSpaceStatisticsPropertiesCount"),
Uint32::NewFromUnsigned(env->isolate(),
kHeapSpaceStatisticsPropertiesCount));

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

// Heap space names are extracted once and exposed to JavaScript to
// avoid excessive creation of heap space name Strings.
HeapSpaceStatistics s;
const Local<Array> heap_spaces = Array::New(env->isolate(),
number_of_heap_spaces);
for (size_t i = 0; i < number_of_heap_spaces; i++) {
env->isolate()->GetHeapSpaceStatistics(&s, i);
Local<String> heap_space_name = String::NewFromUtf8(env->isolate(),
s.space_name(),
NewStringType::kNormal)
.ToLocalChecked();
heap_spaces->Set(i, heap_space_name);
}
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kHeapSpaces"),
heap_spaces);

env->SetMethod(target,
"updateHeapSpaceStatisticsArrayBuffer",
UpdateHeapSpaceStatisticsBuffer);

env->set_heap_space_statistics_buffer(
new uint32_t[kHeapSpaceStatisticsPropertiesCount * number_of_heap_spaces]);

const size_t heap_space_statistics_buffer_byte_length =
sizeof(*env->heap_space_statistics_buffer()) *
kHeapSpaceStatisticsPropertiesCount *
number_of_heap_spaces;

target->Set(FIXED_ONE_BYTE_STRING(env->isolate(),
"heapSpaceStatisticsArrayBuffer"),
ArrayBuffer::New(env->isolate(),
env->heap_space_statistics_buffer(),
heap_space_statistics_buffer_byte_length));

#define V(i, _, name) \
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), #name), \
Uint32::NewFromUnsigned(env->isolate(), i));

HEAP_SPACE_STATISTICS_PROPERTIES(V)
#undef V

env->SetMethod(target, "setFlagsFromString", SetFlagsFromString);
}

} // namespace node
Expand Down
19 changes: 19 additions & 0 deletions test/parallel/test-v8-stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,22 @@ assert.deepEqual(Object.keys(s).sort(), keys);
keys.forEach(function(key) {
assert.equal(typeof s[key], 'number');
});


const expectedHeapSpaces = [
'new_space',
'old_space',
'code_space',
'map_space',
'large_object_space'
];
const heapSpaceStatistics = v8.getHeapSpaceStatistics();
const actualHeapSpaceNames = heapSpaceStatistics.map((s) => s.space_name);
assert.deepEqual(actualHeapSpaceNames.sort(), expectedHeapSpaces.sort());
heapSpaceStatistics.forEach((heapSpace) => {
assert.strictEqual(typeof heapSpace.space_name, 'string');
assert.strictEqual(typeof heapSpace.space_size, 'number');
assert.strictEqual(typeof heapSpace.space_used_size, 'number');
assert.strictEqual(typeof heapSpace.space_available_size, 'number');
assert.strictEqual(typeof heapSpace.physical_space_size, 'number');
});

0 comments on commit 504b01b

Please sign in to comment.