%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/memory-measurement.cc |
// 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 "include/v8-local-handle.h"
#include "src/api/api-inl.h"
#include "src/execution/isolate-inl.h"
#include "src/handles/global-handles-inl.h"
#include "src/heap/factory-inl.h"
#include "src/heap/incremental-marking.h"
#include "src/heap/marking-worklist.h"
#include "src/logging/counters.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-promise-inl.h"
#include "src/objects/smi.h"
#include "src/tasks/task-utils.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-engine.h"
#endif
namespace v8 {
namespace internal {
namespace {
class MemoryMeasurementResultBuilder {
public:
MemoryMeasurementResultBuilder(Isolate* isolate, Factory* factory)
: isolate_(isolate), factory_(factory) {
result_ = NewJSObject();
}
void AddTotal(size_t estimate, size_t lower_bound, size_t upper_bound) {
AddProperty(result_, factory_->total_string(),
NewResult(estimate, lower_bound, upper_bound));
}
void AddCurrent(size_t estimate, size_t lower_bound, size_t upper_bound) {
detailed_ = true;
AddProperty(result_, factory_->current_string(),
NewResult(estimate, lower_bound, upper_bound));
}
void AddOther(size_t estimate, size_t lower_bound, size_t upper_bound) {
detailed_ = true;
other_.push_back(NewResult(estimate, lower_bound, upper_bound));
}
void AddWasm(size_t code, size_t metadata) {
Handle<JSObject> wasm = NewJSObject();
AddProperty(wasm, factory_->NewStringFromAsciiChecked("code"),
NewNumber(code));
AddProperty(wasm, factory_->NewStringFromAsciiChecked("metadata"),
NewNumber(metadata));
AddProperty(result_, factory_->NewStringFromAsciiChecked("WebAssembly"),
wasm);
}
Handle<JSObject> Build() {
if (detailed_) {
int length = static_cast<int>(other_.size());
Handle<FixedArray> other = factory_->NewFixedArray(length);
for (int i = 0; i < length; i++) {
other->set(i, *other_[i]);
}
AddProperty(result_, factory_->other_string(),
factory_->NewJSArrayWithElements(other));
}
return result_;
}
private:
Handle<JSObject> NewResult(size_t estimate, size_t lower_bound,
size_t upper_bound) {
Handle<JSObject> result = NewJSObject();
Handle<Object> estimate_obj = NewNumber(estimate);
AddProperty(result, factory_->jsMemoryEstimate_string(), estimate_obj);
Handle<Object> range = NewRange(lower_bound, upper_bound);
AddProperty(result, factory_->jsMemoryRange_string(), range);
return result;
}
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_;
std::vector<Handle<JSObject>> other_;
bool detailed_ = false;
};
} // anonymous namespace
class V8_EXPORT_PRIVATE MeasureMemoryDelegate
: public v8::MeasureMemoryDelegate {
public:
MeasureMemoryDelegate(Isolate* isolate, Handle<NativeContext> context,
Handle<JSPromise> promise, v8::MeasureMemoryMode mode);
~MeasureMemoryDelegate() override;
// v8::MeasureMemoryDelegate overrides:
bool ShouldMeasure(v8::Local<v8::Context> context) override;
void MeasurementComplete(Result result) override;
private:
Isolate* isolate_;
Handle<JSPromise> promise_;
Handle<NativeContext> context_;
v8::MeasureMemoryMode mode_;
};
MeasureMemoryDelegate::MeasureMemoryDelegate(Isolate* isolate,
Handle<NativeContext> context,
Handle<JSPromise> promise,
v8::MeasureMemoryMode mode)
: isolate_(isolate), mode_(mode) {
context_ = isolate->global_handles()->Create(*context);
promise_ = isolate->global_handles()->Create(*promise);
}
MeasureMemoryDelegate::~MeasureMemoryDelegate() {
isolate_->global_handles()->Destroy(promise_.location());
isolate_->global_handles()->Destroy(context_.location());
}
bool MeasureMemoryDelegate::ShouldMeasure(v8::Local<v8::Context> context) {
Handle<NativeContext> native_context =
Handle<NativeContext>::cast(Utils::OpenHandle(*context));
return context_->security_token() == native_context->security_token();
}
void MeasureMemoryDelegate::MeasurementComplete(Result result) {
size_t shared_size = result.unattributed_size_in_bytes;
size_t wasm_code = result.wasm_code_size_in_bytes;
size_t wasm_metadata = result.wasm_metadata_size_in_bytes;
v8::Local<v8::Context> v8_context =
Utils::Convert<HeapObject, v8::Context>(context_);
v8::Context::Scope scope(v8_context);
size_t total_size = 0;
size_t current_size = 0;
for (const auto& context_and_size : result.context_sizes_in_bytes) {
total_size += context_and_size.second;
if (*Utils::OpenHandle(*context_and_size.first) == *context_) {
current_size = context_and_size.second;
}
}
MemoryMeasurementResultBuilder result_builder(isolate_, isolate_->factory());
result_builder.AddTotal(total_size, total_size, total_size + shared_size);
if (wasm_code > 0 || wasm_metadata > 0) {
result_builder.AddWasm(wasm_code, wasm_metadata);
}
if (mode_ == v8::MeasureMemoryMode::kDetailed) {
result_builder.AddCurrent(current_size, current_size,
current_size + shared_size);
for (const auto& context_and_size : result.context_sizes_in_bytes) {
if (*Utils::OpenHandle(*context_and_size.first) != *context_) {
size_t other_size = context_and_size.second;
result_builder.AddOther(other_size, other_size,
other_size + shared_size);
}
}
}
Handle<JSObject> jsresult = result_builder.Build();
JSPromise::Resolve(promise_, jsresult).ToHandleChecked();
}
MemoryMeasurement::MemoryMeasurement(Isolate* isolate)
: isolate_(isolate),
task_runner_(isolate->heap()->GetForegroundTaskRunner()),
random_number_generator_() {
if (v8_flags.random_seed) {
random_number_generator_.SetSeed(v8_flags.random_seed);
}
}
bool MemoryMeasurement::EnqueueRequest(
std::unique_ptr<v8::MeasureMemoryDelegate> delegate,
v8::MeasureMemoryExecution execution,
const std::vector<Handle<NativeContext>> contexts) {
int length = static_cast<int>(contexts.size());
Handle<WeakFixedArray> weak_contexts =
isolate_->factory()->NewWeakFixedArray(length);
for (int i = 0; i < length; ++i) {
weak_contexts->Set(i, HeapObjectReference::Weak(*contexts[i]));
}
Handle<WeakFixedArray> global_weak_contexts =
isolate_->global_handles()->Create(*weak_contexts);
Request request = {std::move(delegate), // delegate
global_weak_contexts, // contexts
std::vector<size_t>(length), // sizes
0u, // shared
0u, // wasm_code
0u, // wasm_metadata
{}}; // timer
request.timer.Start();
received_.push_back(std::move(request));
ScheduleGCTask(execution);
return true;
}
std::vector<Address> MemoryMeasurement::StartProcessing() {
if (received_.empty()) return {};
std::unordered_set<Address> unique_contexts;
DCHECK(processing_.empty());
processing_ = std::move(received_);
for (const auto& request : processing_) {
Handle<WeakFixedArray> contexts = request.contexts;
for (int i = 0; i < contexts->length(); i++) {
Tagged<HeapObject> context;
if (contexts->Get(i).GetHeapObject(&context)) {
unique_contexts.insert(context.ptr());
}
}
}
return std::vector<Address>(unique_contexts.begin(), unique_contexts.end());
}
void MemoryMeasurement::FinishProcessing(const NativeContextStats& stats) {
if (processing_.empty()) return;
size_t shared = stats.Get(MarkingWorklists::kSharedContext);
#if V8_ENABLE_WEBASSEMBLY
size_t wasm_code = wasm::GetWasmCodeManager()->committed_code_space();
size_t wasm_metadata =
wasm::GetWasmEngine()->EstimateCurrentMemoryConsumption();
#endif
while (!processing_.empty()) {
Request request = std::move(processing_.front());
processing_.pop_front();
for (int i = 0; i < static_cast<int>(request.sizes.size()); i++) {
Tagged<HeapObject> context;
if (!request.contexts->Get(i).GetHeapObject(&context)) {
continue;
}
request.sizes[i] = stats.Get(context.ptr());
}
request.shared = shared;
#if V8_ENABLE_WEBASSEMBLY
request.wasm_code = wasm_code;
request.wasm_metadata = wasm_metadata;
#endif
done_.push_back(std::move(request));
}
ScheduleReportingTask();
}
void MemoryMeasurement::ScheduleReportingTask() {
if (reporting_task_pending_) return;
reporting_task_pending_ = true;
task_runner_->PostTask(MakeCancelableTask(isolate_, [this] {
reporting_task_pending_ = false;
ReportResults();
}));
}
bool MemoryMeasurement::IsGCTaskPending(v8::MeasureMemoryExecution execution) {
DCHECK(execution == v8::MeasureMemoryExecution::kEager ||
execution == v8::MeasureMemoryExecution::kDefault);
return execution == v8::MeasureMemoryExecution::kEager
? eager_gc_task_pending_
: delayed_gc_task_pending_;
}
void MemoryMeasurement::SetGCTaskPending(v8::MeasureMemoryExecution execution) {
DCHECK(execution == v8::MeasureMemoryExecution::kEager ||
execution == v8::MeasureMemoryExecution::kDefault);
if (execution == v8::MeasureMemoryExecution::kEager) {
eager_gc_task_pending_ = true;
} else {
delayed_gc_task_pending_ = true;
}
}
void MemoryMeasurement::SetGCTaskDone(v8::MeasureMemoryExecution execution) {
DCHECK(execution == v8::MeasureMemoryExecution::kEager ||
execution == v8::MeasureMemoryExecution::kDefault);
if (execution == v8::MeasureMemoryExecution::kEager) {
eager_gc_task_pending_ = false;
} else {
delayed_gc_task_pending_ = false;
}
}
void MemoryMeasurement::ScheduleGCTask(v8::MeasureMemoryExecution execution) {
if (execution == v8::MeasureMemoryExecution::kLazy) return;
if (IsGCTaskPending(execution)) return;
SetGCTaskPending(execution);
auto task = MakeCancelableTask(isolate_, [this, execution] {
SetGCTaskDone(execution);
if (received_.empty()) return;
Heap* heap = isolate_->heap();
if (v8_flags.incremental_marking) {
if (heap->incremental_marking()->IsStopped()) {
heap->StartIncrementalMarking(GCFlag::kNoFlags,
GarbageCollectionReason::kMeasureMemory);
} else {
if (execution == v8::MeasureMemoryExecution::kEager) {
heap->FinalizeIncrementalMarkingAtomically(
GarbageCollectionReason::kMeasureMemory);
}
ScheduleGCTask(execution);
}
} else {
heap->CollectGarbage(OLD_SPACE, GarbageCollectionReason::kMeasureMemory);
}
});
if (execution == v8::MeasureMemoryExecution::kEager) {
task_runner_->PostTask(std::move(task));
} else {
task_runner_->PostDelayedTask(std::move(task), NextGCTaskDelayInSeconds());
}
}
int MemoryMeasurement::NextGCTaskDelayInSeconds() {
return kGCTaskDelayInSeconds +
random_number_generator_.NextInt(kGCTaskDelayInSeconds);
}
void MemoryMeasurement::ReportResults() {
while (!done_.empty()) {
Request request = std::move(done_.front());
done_.pop_front();
HandleScope handle_scope(isolate_);
std::vector<std::pair<v8::Local<v8::Context>, size_t>> sizes;
DCHECK_EQ(request.sizes.size(),
static_cast<size_t>(request.contexts->length()));
for (int i = 0; i < request.contexts->length(); i++) {
Tagged<HeapObject> raw_context;
if (!request.contexts->Get(i).GetHeapObject(&raw_context)) {
continue;
}
v8::Local<v8::Context> context = Utils::Convert<HeapObject, v8::Context>(
handle(raw_context, isolate_));
sizes.push_back(std::make_pair(context, request.sizes[i]));
}
START_ALLOW_USE_DEPRECATED()
// Temporarily call both old and new callbacks.
request.delegate->MeasurementComplete(sizes, request.shared);
END_ALLOW_USE_DEPRECATED()
request.delegate->MeasurementComplete(
{sizes, request.shared, request.wasm_code, request.wasm_metadata});
isolate_->counters()->measure_memory_delay_ms()->AddSample(
static_cast<int>(request.timer.Elapsed().InMilliseconds()));
}
}
std::unique_ptr<v8::MeasureMemoryDelegate> MemoryMeasurement::DefaultDelegate(
Isolate* isolate, Handle<NativeContext> context, Handle<JSPromise> promise,
v8::MeasureMemoryMode mode) {
return std::make_unique<MeasureMemoryDelegate>(isolate, context, promise,
mode);
}
bool NativeContextInferrer::InferForContext(PtrComprCageBase cage_base,
Tagged<Context> context,
Address* native_context) {
Tagged<Map> context_map = context->map(cage_base, kAcquireLoad);
Tagged<Object> maybe_native_context =
TaggedField<Object, Map::kConstructorOrBackPointerOrNativeContextOffset>::
Acquire_Load(cage_base, context_map);
if (IsNativeContext(maybe_native_context, cage_base)) {
*native_context = maybe_native_context.ptr();
return true;
}
return false;
}
bool NativeContextInferrer::InferForJSFunction(PtrComprCageBase cage_base,
Tagged<JSFunction> function,
Address* native_context) {
Tagged<Object> maybe_context =
TaggedField<Object, JSFunction::kContextOffset>::Acquire_Load(cage_base,
function);
// The context may be a smi during deserialization.
if (IsSmi(maybe_context)) {
DCHECK_EQ(maybe_context, Smi::uninitialized_deserialization_value());
return false;
}
if (!IsContext(maybe_context)) {
// The function does not have a context.
return false;
}
return InferForContext(cage_base, Context::cast(maybe_context),
native_context);
}
bool NativeContextInferrer::InferForJSObject(PtrComprCageBase cage_base,
Tagged<Map> map,
Tagged<JSObject> object,
Address* native_context) {
if (map->instance_type() == JS_GLOBAL_OBJECT_TYPE) {
Tagged<Object> maybe_context =
JSGlobalObject::cast(object)->native_context_unchecked(cage_base);
if (IsNativeContext(maybe_context)) {
*native_context = maybe_context.ptr();
return true;
}
}
// The maximum number of steps to perform when looking for the context.
const int kMaxSteps = 3;
Tagged<Object> maybe_constructor =
map->TryGetConstructor(cage_base, kMaxSteps);
if (IsJSFunction(maybe_constructor)) {
return InferForJSFunction(cage_base, JSFunction::cast(maybe_constructor),
native_context);
}
return false;
}
void NativeContextStats::Clear() { size_by_context_.clear(); }
void NativeContextStats::Merge(const NativeContextStats& other) {
for (const auto& it : other.size_by_context_) {
size_by_context_[it.first] += it.second;
}
}
void NativeContextStats::IncrementExternalSize(Address context, Tagged<Map> map,
Tagged<HeapObject> object) {
InstanceType instance_type = map->instance_type();
size_t external_size = 0;
if (instance_type == JS_ARRAY_BUFFER_TYPE) {
external_size = JSArrayBuffer::cast(object)->GetByteLength();
} else {
DCHECK(InstanceTypeChecker::IsExternalString(instance_type));
external_size = ExternalString::cast(object)->ExternalPayloadSize();
}
size_by_context_[context] += external_size;
}
} // namespace internal
} // namespace v8