summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Ripkens <bripkens.dev@gmail.com>2015-12-29 11:54:35 +0100
committercjihrig <cjihrig@gmail.com>2016-01-18 11:44:00 -0500
commit5f57005ec9eacbfc7855ec7c9e246f61da5a75ae (patch)
tree2daff450b026b0d18faa494888319db7d9048393
parente65f1f7954fb364ab70063cdb374603c82f58385 (diff)
downloadandroid-node-v8-5f57005ec9eacbfc7855ec7c9e246f61da5a75ae.tar.gz
android-node-v8-5f57005ec9eacbfc7855ec7c9e246f61da5a75ae.tar.bz2
android-node-v8-5f57005ec9eacbfc7855ec7c9e246f61da5a75ae.zip
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: https://github.com/nodejs/node/issues/2079 PR-URL: https://github.com/nodejs/node/pull/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>
-rw-r--r--doc/api/v8.markdown49
-rw-r--r--lib/v8.js33
-rw-r--r--src/env-inl.h12
-rw-r--r--src/env.h4
-rw-r--r--src/node_v8.cc87
-rw-r--r--test/parallel/test-v8-stats.js19
6 files changed, 202 insertions, 2 deletions
diff --git a/doc/api/v8.markdown b/doc/api/v8.markdown
index 70abd6c647..c6d760b0d5 100644
--- a/doc/api/v8.markdown
+++ b/doc/api/v8.markdown
@@ -21,6 +21,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)
Set additional V8 command line flags. Use with care; changing settings
diff --git a/lib/v8.js b/lib/v8.js
index acadfa64e0..551b2ada98 100644
--- a/lib/v8.js
+++ b/lib/v8.js
@@ -16,9 +16,9 @@
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;
@@ -26,6 +26,18 @@ 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;
@@ -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 (let 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;
+};
diff --git a/src/env-inl.h b/src/env-inl.h
index f73e9c6ba2..d2c0e048a6 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -242,6 +242,7 @@ inline Environment::~Environment() {
isolate_data()->Put();
delete[] heap_statistics_buffer_;
+ delete[] heap_space_statistics_buffer_;
delete[] http_parser_buffer_;
}
@@ -374,6 +375,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_;
}
diff --git a/src/env.h b/src/env.h
index 743bf057e8..bb0868e1d8 100644
--- a/src/env.h
+++ b/src/env.h
@@ -462,6 +462,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);
@@ -562,6 +565,7 @@ class Environment {
int handle_cleanup_waiting_;
uint32_t* heap_statistics_buffer_ = nullptr;
+ uint32_t* heap_space_statistics_buffer_ = nullptr;
char* http_parser_buffer_;
diff --git a/src/node_v8.cc b/src/node_v8.cc
index 9f456daec4..a1122e57f1 100644
--- a/src/node_v8.cc
+++ b/src/node_v8.cc
@@ -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;
@@ -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;
@@ -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);
@@ -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]);
@@ -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
diff --git a/test/parallel/test-v8-stats.js b/test/parallel/test-v8-stats.js
index eb5566fe2b..54140e4110 100644
--- a/test/parallel/test-v8-stats.js
+++ b/test/parallel/test-v8-stats.js
@@ -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');
+});