[api, heap] Add v8::Isolate::MeasureMemory API

This adds a new API function and provides a simple implementation
of performance.measureMemory() in d8. The implementation currently
immediately resolves the result promise with the current heap size.

Bug: chromium:973627

Change-Id: Ia8e1963a49b7df628b5487a2c0d601473f0cb039
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1796502
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63694}
This commit is contained in:
Ulan Degenbaev 2019-09-11 18:37:40 +02:00 committed by Commit Bot
parent 134e110211
commit e9730043cf
12 changed files with 285 additions and 52 deletions

View File

@ -2318,6 +2318,8 @@ v8_source_set("v8_base_without_compiler") {
"src/heap/mark-compact.h",
"src/heap/marking.cc",
"src/heap/marking.h",
"src/heap/memory-measurement.cc",
"src/heap/memory-measurement.h",
"src/heap/memory-reducer.cc",
"src/heap/memory-reducer.h",
"src/heap/object-stats.cc",

View File

@ -7569,6 +7569,8 @@ struct DeserializeInternalFieldsCallback {
};
typedef DeserializeInternalFieldsCallback DeserializeEmbedderFieldsCallback;
enum class MeasureMemoryMode { kSummary, kDetailed };
/**
* Isolate represents an isolated instance of the V8 engine. V8 isolates have
* completely separate states. Objects from one isolate must not be used in
@ -8089,6 +8091,17 @@ class V8_EXPORT Isolate {
*/
bool GetHeapCodeAndMetadataStatistics(HeapCodeStatistics* object_statistics);
/**
* Enqueues a memory measurement request for the given context and mode.
* This API is experimental and may change significantly.
*
* \param mode Indicates whether the result should include per-context
* memory usage or just the total memory usage.
* \returns a promise that will be resolved with memory usage estimate.
*/
v8::MaybeLocal<v8::Promise> MeasureMemory(v8::Local<v8::Context> context,
MeasureMemoryMode mode);
/**
* Get a call stack sample from the isolate.
* \param state Execution state.

View File

@ -8350,6 +8350,15 @@ bool Isolate::GetHeapCodeAndMetadataStatistics(
return true;
}
v8::MaybeLocal<v8::Promise> Isolate::MeasureMemory(
v8::Local<v8::Context> context, MeasureMemoryMode mode) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
i::Handle<i::NativeContext> native_context =
handle(Utils::OpenHandle(*context)->native_context(), isolate);
return v8::Utils::PromiseToLocal(
isolate->heap()->MeasureMemory(native_context, mode));
}
void Isolate::GetStackSample(const RegisterState& state, void** frames,
size_t frames_limit, SampleInfo* sample_info) {
RegisterState regs = state;

View File

@ -218,12 +218,20 @@ static Local<Value> Throw(Isolate* isolate, const char* message) {
.ToLocalChecked());
}
static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
Local<v8::Object> object, const char* property) {
static MaybeLocal<Value> TryGetValue(v8::Isolate* isolate,
Local<Context> context,
Local<v8::Object> object,
const char* property) {
Local<String> v8_str =
String::NewFromUtf8(isolate, property, NewStringType::kNormal)
.ToLocalChecked();
return object->Get(context, v8_str).ToLocalChecked();
.FromMaybe(Local<String>());
if (v8_str.IsEmpty()) return Local<Value>();
return object->Get(context, v8_str);
}
static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
Local<v8::Object> object, const char* property) {
return TryGetValue(isolate, context, object, property).ToLocalChecked();
}
Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) {
@ -989,6 +997,27 @@ void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
}
// performance.measureMemory() implements JavaScript Memory API proposal.
// See https://github.com/ulan/javascript-agent-memory/blob/master/explainer.md.
void Shell::PerformanceMeasureMemory(
const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::MeasureMemoryMode mode = v8::MeasureMemoryMode::kSummary;
v8::Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.Length() >= 1 && args[0]->IsObject()) {
Local<Object> object = args[0].As<Object>();
Local<Value> value = TryGetValue(isolate, context, object, "detailed")
.FromMaybe(Local<Value>());
if (!value.IsEmpty() && value->IsBoolean() &&
value->BooleanValue(isolate)) {
mode = v8::MeasureMemoryMode::kDetailed;
}
}
v8::MaybeLocal<v8::Promise> result =
args.GetIsolate()->MeasureMemory(context, mode);
args.GetReturnValue().Set(result.FromMaybe(v8::Local<v8::Promise>()));
}
// Realm.current() returns the index of the currently active realm.
void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
@ -1825,6 +1854,10 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
String::NewFromUtf8(isolate, "now", NewStringType::kNormal)
.ToLocalChecked(),
FunctionTemplate::New(isolate, PerformanceNow));
performance_template->Set(
String::NewFromUtf8(isolate, "measureMemory", NewStringType::kNormal)
.ToLocalChecked(),
FunctionTemplate::New(isolate, PerformanceMeasureMemory));
global_template->Set(
String::NewFromUtf8(isolate, "performance", NewStringType::kNormal)
.ToLocalChecked(),

View File

@ -391,6 +391,8 @@ class Shell : public i::AllStatic {
static void MapCounters(v8::Isolate* isolate, const char* name);
static void PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args);
static void PerformanceMeasureMemory(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args);
static void RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args);

View File

@ -39,6 +39,7 @@
#include "src/heap/incremental-marking.h"
#include "src/heap/mark-compact-inl.h"
#include "src/heap/mark-compact.h"
#include "src/heap/memory-measurement.h"
#include "src/heap/memory-reducer.h"
#include "src/heap/object-stats.h"
#include "src/heap/objects-visiting-inl.h"
@ -3760,6 +3761,11 @@ bool Heap::InvokeNearHeapLimitCallback() {
return false;
}
Handle<JSPromise> Heap::MeasureMemory(Handle<NativeContext> context,
v8::MeasureMemoryMode mode) {
return memory_measurement_->EnqueueRequest(context, mode);
}
void Heap::CollectCodeStatistics() {
TRACE_EVENT0("v8", "Heap::CollectCodeStatistics");
CodeStatistics::ResetCodeAndMetadataStatistics(isolate());
@ -5051,6 +5057,7 @@ void Heap::SetUpSpaces() {
#endif // ENABLE_MINOR_MC
array_buffer_collector_.reset(new ArrayBufferCollector(this));
gc_idle_time_handler_.reset(new GCIdleTimeHandler());
memory_measurement_.reset(new MemoryMeasurement(isolate()));
memory_reducer_.reset(new MemoryReducer(this));
if (V8_UNLIKELY(TracingFlags::is_gc_stats_enabled())) {
live_object_stats_.reset(new ObjectStats(this));

View File

@ -47,6 +47,9 @@ class TestMemoryAllocatorScope;
class IncrementalMarking;
class BackingStore;
class JSArrayBuffer;
class JSPromise;
class NativeContext;
using v8::MemoryPressureLevel;
class AllocationObserver;
@ -63,6 +66,7 @@ class Isolate;
class JSFinalizationGroup;
class LocalEmbedderHeapTracer;
class MemoryAllocator;
class MemoryMeasurement;
class MemoryReducer;
class MinorMarkCompactCollector;
class ObjectIterator;
@ -563,6 +567,9 @@ class Heap {
void RecordStats(HeapStats* stats, bool take_snapshot = false);
Handle<JSPromise> MeasureMemory(Handle<NativeContext> context,
v8::MeasureMemoryMode mode);
// Check new space expansion criteria and expand semispaces if it was hit.
void CheckNewSpaceExpansionCriteria();
@ -1991,6 +1998,7 @@ class Heap {
std::unique_ptr<IncrementalMarking> incremental_marking_;
std::unique_ptr<ConcurrentMarking> concurrent_marking_;
std::unique_ptr<GCIdleTimeHandler> gc_idle_time_handler_;
std::unique_ptr<MemoryMeasurement> memory_measurement_;
std::unique_ptr<MemoryReducer> memory_reducer_;
std::unique_ptr<ObjectStats> live_object_stats_;
std::unique_ptr<ObjectStats> dead_object_stats_;

View File

@ -0,0 +1,80 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/heap/memory-measurement.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/isolate.h"
#include "src/heap/factory-inl.h"
#include "src/heap/factory.h"
#include "src/objects/js-promise.h"
namespace v8 {
namespace internal {
MemoryMeasurement::MemoryMeasurement(Isolate* isolate) : isolate_(isolate) {}
namespace {
class MemoryMeasurementResultBuilder {
public:
MemoryMeasurementResultBuilder(Isolate* isolate, Factory* factory)
: isolate_(isolate), factory_(factory) {
result_ = NewJSObject();
}
void AddTotals(size_t estimate, size_t lower_bound, size_t upper_bound) {
Handle<JSObject> total = NewJSObject();
Handle<Object> estimate_obj = NewNumber(estimate);
AddProperty(total, factory_->jsMemoryEstimate_string(), estimate_obj);
Handle<Object> range = NewRange(lower_bound, upper_bound);
AddProperty(total, factory_->jsMemoryRange_string(), range);
AddProperty(result_, factory_->total_string(), total);
}
Handle<JSObject> Build() { return result_; }
private:
Handle<Object> NewNumber(size_t value) {
return factory_->NewNumberFromSize(value);
}
Handle<JSObject> NewJSObject() {
return factory_->NewJSObject(isolate_->object_function());
}
Handle<JSArray> NewRange(size_t lower_bound, size_t upper_bound) {
Handle<Object> lower = NewNumber(lower_bound);
Handle<Object> upper = NewNumber(upper_bound);
Handle<FixedArray> elements = factory_->NewFixedArray(2);
elements->set(0, *lower);
elements->set(1, *upper);
return factory_->NewJSArrayWithElements(elements);
}
void AddProperty(Handle<JSObject> object, Handle<String> name,
Handle<Object> value) {
JSObject::AddProperty(isolate_, object, name, value, NONE);
}
Isolate* isolate_;
Factory* factory_;
Handle<JSObject> result_;
};
} // anonymous namespace
Handle<JSPromise> MemoryMeasurement::EnqueueRequest(
Handle<NativeContext> context, v8::MeasureMemoryMode mode) {
Handle<JSPromise> promise = isolate_->factory()->NewJSPromise();
MemoryMeasurementResultBuilder result_builder(isolate_, isolate_->factory());
result_builder.AddTotals(isolate_->heap()->SizeOfObjects(), 0,
isolate_->heap()->SizeOfObjects());
Handle<JSObject> result = result_builder.Build();
JSPromise::Resolve(promise, result).ToHandleChecked();
return promise;
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,29 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_HEAP_MEMORY_MEASUREMENT_H_
#define V8_HEAP_MEMORY_MEASUREMENT_H_
#include "src/common/globals.h"
#include "src/objects/objects.h"
namespace v8 {
namespace internal {
class Heap;
class V8_EXPORT_PRIVATE MemoryMeasurement {
public:
explicit MemoryMeasurement(Isolate* isolate);
Handle<JSPromise> EnqueueRequest(Handle<NativeContext> context,
v8::MeasureMemoryMode mode);
private:
Isolate* isolate_;
};
} // namespace internal
} // namespace v8
#endif // V8_HEAP_MEMORY_MEASUREMENT_H_

View File

@ -210,6 +210,8 @@
V(_, Int32Array_string, "Int32Array") \
V(_, Int8Array_string, "Int8Array") \
V(_, isExtensible_string, "isExtensible") \
V(_, jsMemoryEstimate_string, "jsMemoryEstimate") \
V(_, jsMemoryRange_string, "jsMemoryRange") \
V(_, keys_string, "keys") \
V(_, lastIndex_string, "lastIndex") \
V(_, length_string, "length") \
@ -300,6 +302,7 @@
V(_, toJSON_string, "toJSON") \
V(_, toString_string, "toString") \
V(_, true_string, "true") \
V(_, total_string, "total") \
V(_, TypeError_string, "TypeError") \
V(_, Uint16Array_string, "Uint16Array") \
V(_, Uint32Array_string, "Uint32Array") \

View File

@ -0,0 +1,47 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Test the performance.measureMemory() function of d8. This test only makes
// sense with d8.
load('test/mjsunit/mjsunit.js');
function assertLessThanOrEqual(a, b) {
assertTrue(a <= b, `Expected ${a} <= ${b}`);
}
function checkMeasureMemoryResult(result) {
assertTrue('total' in result);
assertTrue('jsMemoryEstimate' in result.total);
assertTrue('jsMemoryRange' in result.total);
assertEquals('number', typeof result.total.jsMemoryEstimate);
assertEquals(2, result.total.jsMemoryRange.length);
assertEquals('number', typeof result.total.jsMemoryRange[0]);
assertEquals('number', typeof result.total.jsMemoryRange[1]);
assertLessThanOrEqual(result.total.jsMemoryRange[0],
result.total.jsMemoryRange[1]);
assertLessThanOrEqual(result.total.jsMemoryRange[0],
result.total.jsMemoryEstimate);
assertLessThanOrEqual(result.total.jsMemoryEstimate,
result.total.jsMemoryRange[1]);
}
if (this.performance && performance.measureMemory) {
assertPromiseResult((async () => {
let result = await performance.measureMemory();
checkMeasureMemoryResult(result);
})());
assertPromiseResult((async () => {
let result = await performance.measureMemory({detailed: false});
checkMeasureMemoryResult(result);
})());
assertPromiseResult((async () => {
let result = await performance.measureMemory({detailed: true});
// TODO(ulan): Also check the detailed results once measureMemory
// supports them.
checkMeasureMemoryResult(result);
})());
}

View File

@ -291,54 +291,54 @@ KNOWN_MAPS = {
("read_only_space", 0x023a1): (87, "EnumCacheMap"),
("read_only_space", 0x02441): (82, "ArrayBoilerplateDescriptionMap"),
("read_only_space", 0x02631): (90, "InterceptorInfoMap"),
("read_only_space", 0x04ef9): (77, "AccessCheckInfoMap"),
("read_only_space", 0x04f49): (78, "AccessorInfoMap"),
("read_only_space", 0x04f99): (79, "AccessorPairMap"),
("read_only_space", 0x04fe9): (80, "AliasedArgumentsEntryMap"),
("read_only_space", 0x05039): (81, "AllocationMementoMap"),
("read_only_space", 0x05089): (83, "AsmWasmDataMap"),
("read_only_space", 0x050d9): (84, "AsyncGeneratorRequestMap"),
("read_only_space", 0x05129): (85, "ClassPositionsMap"),
("read_only_space", 0x05179): (86, "DebugInfoMap"),
("read_only_space", 0x051c9): (88, "FunctionTemplateInfoMap"),
("read_only_space", 0x05219): (89, "FunctionTemplateRareDataMap"),
("read_only_space", 0x05269): (91, "InterpreterDataMap"),
("read_only_space", 0x052b9): (92, "ObjectTemplateInfoMap"),
("read_only_space", 0x05309): (93, "PromiseCapabilityMap"),
("read_only_space", 0x05359): (94, "PromiseReactionMap"),
("read_only_space", 0x053a9): (95, "PrototypeInfoMap"),
("read_only_space", 0x053f9): (96, "ScriptMap"),
("read_only_space", 0x05449): (97, "SourcePositionTableWithFrameCacheMap"),
("read_only_space", 0x05499): (98, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x054e9): (99, "StackFrameInfoMap"),
("read_only_space", 0x05539): (100, "StackTraceFrameMap"),
("read_only_space", 0x05589): (101, "TemplateObjectDescriptionMap"),
("read_only_space", 0x055d9): (102, "Tuple2Map"),
("read_only_space", 0x05629): (103, "Tuple3Map"),
("read_only_space", 0x05679): (104, "WasmCapiFunctionDataMap"),
("read_only_space", 0x056c9): (105, "WasmDebugInfoMap"),
("read_only_space", 0x05719): (106, "WasmExceptionTagMap"),
("read_only_space", 0x05769): (107, "WasmExportedFunctionDataMap"),
("read_only_space", 0x057b9): (108, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x05809): (109, "WasmJSFunctionDataMap"),
("read_only_space", 0x05859): (110, "CallableTaskMap"),
("read_only_space", 0x058a9): (111, "CallbackTaskMap"),
("read_only_space", 0x058f9): (112, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x05949): (113, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x05999): (114, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x059e9): (115, "InternalClassMap"),
("read_only_space", 0x05a39): (116, "SmiPairMap"),
("read_only_space", 0x05a89): (117, "SmiBoxMap"),
("read_only_space", 0x05ad9): (118, "SortStateMap"),
("read_only_space", 0x05b29): (121, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05b79): (121, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05bc9): (156, "LoadHandler1Map"),
("read_only_space", 0x05c19): (156, "LoadHandler2Map"),
("read_only_space", 0x05c69): (156, "LoadHandler3Map"),
("read_only_space", 0x05cb9): (164, "StoreHandler0Map"),
("read_only_space", 0x05d09): (164, "StoreHandler1Map"),
("read_only_space", 0x05d59): (164, "StoreHandler2Map"),
("read_only_space", 0x05da9): (164, "StoreHandler3Map"),
("read_only_space", 0x04f51): (77, "AccessCheckInfoMap"),
("read_only_space", 0x04fa1): (78, "AccessorInfoMap"),
("read_only_space", 0x04ff1): (79, "AccessorPairMap"),
("read_only_space", 0x05041): (80, "AliasedArgumentsEntryMap"),
("read_only_space", 0x05091): (81, "AllocationMementoMap"),
("read_only_space", 0x050e1): (83, "AsmWasmDataMap"),
("read_only_space", 0x05131): (84, "AsyncGeneratorRequestMap"),
("read_only_space", 0x05181): (85, "ClassPositionsMap"),
("read_only_space", 0x051d1): (86, "DebugInfoMap"),
("read_only_space", 0x05221): (88, "FunctionTemplateInfoMap"),
("read_only_space", 0x05271): (89, "FunctionTemplateRareDataMap"),
("read_only_space", 0x052c1): (91, "InterpreterDataMap"),
("read_only_space", 0x05311): (92, "ObjectTemplateInfoMap"),
("read_only_space", 0x05361): (93, "PromiseCapabilityMap"),
("read_only_space", 0x053b1): (94, "PromiseReactionMap"),
("read_only_space", 0x05401): (95, "PrototypeInfoMap"),
("read_only_space", 0x05451): (96, "ScriptMap"),
("read_only_space", 0x054a1): (97, "SourcePositionTableWithFrameCacheMap"),
("read_only_space", 0x054f1): (98, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x05541): (99, "StackFrameInfoMap"),
("read_only_space", 0x05591): (100, "StackTraceFrameMap"),
("read_only_space", 0x055e1): (101, "TemplateObjectDescriptionMap"),
("read_only_space", 0x05631): (102, "Tuple2Map"),
("read_only_space", 0x05681): (103, "Tuple3Map"),
("read_only_space", 0x056d1): (104, "WasmCapiFunctionDataMap"),
("read_only_space", 0x05721): (105, "WasmDebugInfoMap"),
("read_only_space", 0x05771): (106, "WasmExceptionTagMap"),
("read_only_space", 0x057c1): (107, "WasmExportedFunctionDataMap"),
("read_only_space", 0x05811): (108, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x05861): (109, "WasmJSFunctionDataMap"),
("read_only_space", 0x058b1): (110, "CallableTaskMap"),
("read_only_space", 0x05901): (111, "CallbackTaskMap"),
("read_only_space", 0x05951): (112, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x059a1): (113, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x059f1): (114, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x05a41): (115, "InternalClassMap"),
("read_only_space", 0x05a91): (116, "SmiPairMap"),
("read_only_space", 0x05ae1): (117, "SmiBoxMap"),
("read_only_space", 0x05b31): (118, "SortStateMap"),
("read_only_space", 0x05b81): (121, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05bd1): (121, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05c21): (156, "LoadHandler1Map"),
("read_only_space", 0x05c71): (156, "LoadHandler2Map"),
("read_only_space", 0x05cc1): (156, "LoadHandler3Map"),
("read_only_space", 0x05d11): (164, "StoreHandler0Map"),
("read_only_space", 0x05d61): (164, "StoreHandler1Map"),
("read_only_space", 0x05db1): (164, "StoreHandler2Map"),
("read_only_space", 0x05e01): (164, "StoreHandler3Map"),
("map_space", 0x00119): (1057, "ExternalMap"),
("map_space", 0x00169): (1073, "JSMessageObjectMap"),
}