v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
memory-measurement.cc
Go to the documentation of this file.
1// Copyright 2019 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
8#include "src/api/api-inl.h"
18
19#if V8_ENABLE_WEBASSEMBLY
23#endif
24
25namespace v8::internal {
26namespace {
27// Must only be used from stack.
28//
29// TODO(374253377): This should be implemented purely on the public API and move
30// to d8. There's no reason V8 would need to provide a default delegate on its
31// API.
32class MemoryMeasurementResultBuilder final {
33 public:
34 explicit MemoryMeasurementResultBuilder(v8::Isolate* isolate)
35 : isolate_(reinterpret_cast<Isolate*>(isolate)),
36 factory_(isolate_->factory()) {
37 result_ = NewJSObject();
38 }
39 void AddTotal(size_t estimate, size_t lower_bound, size_t upper_bound) {
40 AddProperty(result_, factory_->total_string(),
41 NewResult(estimate, lower_bound, upper_bound));
42 }
43 void AddCurrent(size_t estimate, size_t lower_bound, size_t upper_bound) {
44 detailed_ = true;
45 AddProperty(result_, factory_->current_string(),
46 NewResult(estimate, lower_bound, upper_bound));
47 }
48 void AddOther(size_t estimate, size_t lower_bound, size_t upper_bound) {
49 detailed_ = true;
50 other_.push_back(NewResult(estimate, lower_bound, upper_bound));
51 }
52 void AddWasm(size_t code, size_t metadata) {
53 DirectHandle<JSObject> wasm = NewJSObject();
54 AddProperty(wasm, factory_->NewStringFromAsciiChecked("code"),
55 NewNumber(code));
56 AddProperty(wasm, factory_->NewStringFromAsciiChecked("metadata"),
57 NewNumber(metadata));
58 AddProperty(result_, factory_->NewStringFromAsciiChecked("WebAssembly"),
59 wasm);
60 }
61 DirectHandle<JSObject> Build() {
62 if (detailed_) {
63 int length = static_cast<int>(other_.size());
64 DirectHandle<FixedArray> other = factory_->NewFixedArray(length);
65 for (int i = 0; i < length; i++) {
66 other->set(i, *other_[i]);
67 }
68 AddProperty(result_, factory_->other_string(),
69 factory_->NewJSArrayWithElements(other));
70 }
71 return result_;
72 }
73
74 private:
75 Handle<JSObject> NewResult(size_t estimate, size_t lower_bound,
76 size_t upper_bound) {
77 Handle<JSObject> result = NewJSObject();
78 DirectHandle<Object> estimate_obj = NewNumber(estimate);
79 AddProperty(result, factory_->jsMemoryEstimate_string(), estimate_obj);
80 DirectHandle<Object> range = NewRange(lower_bound, upper_bound);
81 AddProperty(result, factory_->jsMemoryRange_string(), range);
82 return result;
83 }
84 DirectHandle<Object> NewNumber(size_t value) {
85 return factory_->NewNumberFromSize(value);
86 }
87 Handle<JSObject> NewJSObject() {
88 return factory_->NewJSObject(isolate_->object_function());
89 }
90 DirectHandle<JSArray> NewRange(size_t lower_bound, size_t upper_bound) {
91 DirectHandle<Object> lower = NewNumber(lower_bound);
92 DirectHandle<Object> upper = NewNumber(upper_bound);
93 DirectHandle<FixedArray> elements = factory_->NewFixedArray(2);
94 elements->set(0, *lower);
95 elements->set(1, *upper);
96 return factory_->NewJSArrayWithElements(elements);
97 }
98 void AddProperty(DirectHandle<JSObject> object, DirectHandle<String> name,
99 DirectHandle<Object> value) {
100 JSObject::AddProperty(isolate_, object, name, value, NONE);
101 }
102 Isolate* isolate_;
103 Factory* factory_;
105 std::vector<Handle<JSObject>> other_;
106 bool detailed_ = false;
107};
108} // anonymous namespace
109
112 public:
116 ~MeasureMemoryDelegate() override = default;
117
118 // v8::MeasureMemoryDelegate overrides:
119 bool ShouldMeasure(v8::Local<v8::Context> context) override;
120 void MeasurementComplete(Result result) override;
121
122 private:
127};
128
130 v8::Isolate* isolate, v8::Local<v8::Context> context,
132 : isolate_(isolate),
133 context_(isolate_, context),
134 promise_(isolate_, promise),
135 mode_(mode) {}
136
138 v8::Local<v8::Context> other_context) {
139 return context_.Get(isolate_)->GetSecurityToken() ==
140 other_context->GetSecurityToken();
141}
142
144 size_t shared_size = result.unattributed_size_in_bytes;
145 size_t wasm_code = result.wasm_code_size_in_bytes;
146 size_t wasm_metadata = result.wasm_metadata_size_in_bytes;
147 v8::Local<v8::Context> v8_context = context_.Get(isolate_);
148 v8::Context::Scope scope(v8_context);
149 size_t total_size = 0;
150 size_t current_size = 0;
151 DCHECK_EQ(result.contexts.size(), result.sizes_in_bytes.size());
152 for (size_t i = 0; i < result.contexts.size(); ++i) {
153 total_size += result.sizes_in_bytes[i];
154 if (context_ == result.contexts[i]) {
155 current_size = result.sizes_in_bytes[i];
156 }
157 }
158 MemoryMeasurementResultBuilder result_builder(isolate_);
159 result_builder.AddTotal(total_size, total_size, total_size + shared_size);
160 if (wasm_code > 0 || wasm_metadata > 0) {
161 result_builder.AddWasm(wasm_code, wasm_metadata);
162 }
163
165 result_builder.AddCurrent(current_size, current_size,
166 current_size + shared_size);
167 for (size_t i = 0; i < result.contexts.size(); ++i) {
168 if (context_ != result.contexts[i]) {
169 size_t other_size = result.sizes_in_bytes[i];
170 result_builder.AddOther(other_size, other_size,
171 other_size + shared_size);
172 }
173 }
174 }
175
176 auto v8_result = ToApiHandle<v8::Object>(result_builder.Build());
177 auto v8_promise = promise_.Get(isolate_);
178 if (v8_promise->Resolve(v8_context, v8_result).IsNothing()) {
179 CHECK(reinterpret_cast<Isolate*>(isolate_)->is_execution_terminating());
180 }
181}
182
184 : isolate_(isolate),
185 task_runner_(isolate->heap()->GetForegroundTaskRunner()),
186 random_number_generator_() {
187 if (v8_flags.random_seed) {
188 random_number_generator_.SetSeed(v8_flags.random_seed);
189 }
190}
191
193 std::unique_ptr<v8::MeasureMemoryDelegate> delegate,
195 const std::vector<Handle<NativeContext>> contexts) {
196 int length = static_cast<int>(contexts.size());
197 DirectHandle<WeakFixedArray> weak_contexts =
199 for (int i = 0; i < length; ++i) {
200 weak_contexts->set(i, MakeWeak(*contexts[i]));
201 }
202 Handle<WeakFixedArray> global_weak_contexts =
203 isolate_->global_handles()->Create(*weak_contexts);
204 Request request = {std::move(delegate), // delegate
205 global_weak_contexts, // contexts
206 std::vector<size_t>(length), // sizes
207 0u, // shared
208 0u, // wasm_code
209 0u, // wasm_metadata
210 {}}; // timer
211 request.timer.Start();
212 received_.push_back(std::move(request));
213 ScheduleGCTask(execution);
214 return true;
215}
216
218 if (received_.empty()) return {};
219 std::unordered_set<Address> unique_contexts;
220 DCHECK(processing_.empty());
221 processing_ = std::move(received_);
222 for (const auto& request : processing_) {
223 DirectHandle<WeakFixedArray> contexts = request.contexts;
224 for (int i = 0; i < contexts->length(); i++) {
226 if (contexts->get(i).GetHeapObject(&context)) {
227 unique_contexts.insert(context.ptr());
228 }
229 }
230 }
231 return std::vector<Address>(unique_contexts.begin(), unique_contexts.end());
232}
233
235 if (processing_.empty()) return;
236
237 size_t shared = stats.Get(MarkingWorklists::kSharedContext);
238#if V8_ENABLE_WEBASSEMBLY
239 size_t wasm_code = wasm::GetWasmCodeManager()->committed_code_space();
240 size_t wasm_metadata =
243#endif
244
245 while (!processing_.empty()) {
246 Request request = std::move(processing_.front());
247 processing_.pop_front();
248 for (int i = 0; i < static_cast<int>(request.sizes.size()); i++) {
250 if (!request.contexts->get(i).GetHeapObject(&context)) {
251 continue;
252 }
253 request.sizes[i] = stats.Get(context.ptr());
254 }
255 request.shared = shared;
256#if V8_ENABLE_WEBASSEMBLY
257 request.wasm_code = wasm_code;
258 request.wasm_metadata = wasm_metadata;
259#endif
260 done_.push_back(std::move(request));
261 }
263}
264
273
281
291
301
303 if (execution == v8::MeasureMemoryExecution::kLazy) return;
304 if (IsGCTaskPending(execution)) return;
305 SetGCTaskPending(execution);
306 auto task = MakeCancelableTask(isolate_, [this, execution] {
307 SetGCTaskDone(execution);
308 if (received_.empty()) return;
309 Heap* heap = isolate_->heap();
310 if (v8_flags.incremental_marking) {
311 if (heap->incremental_marking()->IsStopped()) {
312 heap->StartIncrementalMarking(GCFlag::kNoFlags,
314 } else {
315 if (execution == v8::MeasureMemoryExecution::kEager) {
316 heap->FinalizeIncrementalMarkingAtomically(
318 }
319 ScheduleGCTask(execution);
320 }
321 } else {
323 }
324 });
325 if (execution == v8::MeasureMemoryExecution::kEager) {
326 task_runner_->PostTask(std::move(task));
327 } else {
329 }
330}
331
336
338 while (!done_.empty() && !isolate_->is_execution_terminating()) {
339 Request request = std::move(done_.front());
340 done_.pop_front();
341 HandleScope handle_scope(isolate_);
343 reinterpret_cast<v8::Isolate*>(isolate_));
344 std::vector<size_t> size_in_bytes;
345 DCHECK_EQ(request.sizes.size(),
346 static_cast<size_t>(request.contexts->length()));
347 for (int i = 0; i < request.contexts->length(); i++) {
349 if (!request.contexts->get(i).GetHeapObject(&raw_context)) {
350 continue;
351 }
354 contexts.push_back(context);
355 size_in_bytes.push_back(request.sizes[i]);
356 }
358 {{contexts.begin(), contexts.end()},
359 {size_in_bytes.begin(), size_in_bytes.end()},
360 request.shared,
361 request.wasm_code,
362 request.wasm_metadata});
363 isolate_->counters()->measure_memory_delay_ms()->AddSample(
364 static_cast<int>(request.timer.Elapsed().InMilliseconds()));
365 }
366}
367
368std::unique_ptr<v8::MeasureMemoryDelegate> MemoryMeasurement::DefaultDelegate(
369 v8::Isolate* isolate, v8::Local<v8::Context> context,
371 return std::make_unique<MeasureMemoryDelegate>(isolate, context, promise,
372 mode);
373}
374
376
378 for (const auto& it : other.size_by_context_) {
379 size_by_context_[it.first] += it.second;
380 }
381}
382
384 Tagged<HeapObject> object) {
385 InstanceType instance_type = map->instance_type();
386 size_t external_size = 0;
387 if (instance_type == JS_ARRAY_BUFFER_TYPE) {
388 external_size = Cast<JSArrayBuffer>(object)->GetByteLength();
389 } else {
391 external_size = Cast<ExternalString>(object)->ExternalPayloadSize();
392 }
393 size_by_context_[context] += external_size;
394}
395
396} // namespace v8::internal
Isolate * isolate_
virtual void MeasurementComplete(Result result)
void PostDelayedTask(std::unique_ptr< Task > task, double delay_in_seconds, const SourceLocation &location=SourceLocation::Current())
void PostTask(std::unique_ptr< Task > task, const SourceLocation &location=SourceLocation::Current())
Definition v8-platform.h:82
static Local< To > Convert(v8::internal::DirectHandle< From > obj)
Definition api-inl.h:59
TimeDelta Elapsed() const
V8_INLINE int NextInt() V8_WARN_UNUSED_RESULT
int64_t InMilliseconds() const
Definition time.cc:234
Handle< WeakFixedArray > NewWeakFixedArray(int length, AllocationType allocation=AllocationType::kYoung)
IndirectHandle< Object > Create(Tagged< Object > value)
GlobalHandles * global_handles() const
Definition isolate.h:1416
Counters * counters()
Definition isolate.h:1180
v8::internal::Factory * factory()
Definition isolate.h:1527
static V8_EXPORT_PRIVATE void AddProperty(Isolate *isolate, DirectHandle< JSObject > object, DirectHandle< Name > name, DirectHandle< Object > value, PropertyAttributes attributes)
static constexpr Address kSharedContext
const v8::Global< v8::Context > context_
void MeasurementComplete(Result result) override
bool ShouldMeasure(v8::Local< v8::Context > context) override
MeasureMemoryDelegate(v8::Isolate *isolate, v8::Local< v8::Context > context, v8::Local< v8::Promise::Resolver > promise, v8::MeasureMemoryMode mode)
const v8::Global< v8::Promise::Resolver > promise_
~MeasureMemoryDelegate() override=default
std::vector< Address > StartProcessing()
bool EnqueueRequest(std::unique_ptr< v8::MeasureMemoryDelegate > delegate, v8::MeasureMemoryExecution execution, const std::vector< Handle< NativeContext > > contexts)
bool IsGCTaskPending(v8::MeasureMemoryExecution execution)
base::RandomNumberGenerator random_number_generator_
void FinishProcessing(const NativeContextStats &stats)
static std::unique_ptr< v8::MeasureMemoryDelegate > DefaultDelegate(v8::Isolate *isolate, v8::Local< v8::Context > context, v8::Local< v8::Promise::Resolver > promise, v8::MeasureMemoryMode mode)
std::shared_ptr< v8::TaskRunner > task_runner_
void SetGCTaskPending(v8::MeasureMemoryExecution execution)
void ScheduleGCTask(v8::MeasureMemoryExecution execution)
void SetGCTaskDone(v8::MeasureMemoryExecution execution)
std::unordered_map< Address, size_t > size_by_context_
void IncrementExternalSize(Address context, Tagged< Map > map, Tagged< HeapObject > object)
void Merge(const NativeContextStats &other)
size_t EstimateCurrentMemoryConsumption() const
T const result_
RecordWriteMode const mode_
Handle< Context > context_
TNode< Context > context
SharedFunctionInfoRef shared
ZoneVector< RpoNumber > & result
bool detailed_
Factory * factory_
std::vector< Handle< JSObject > > other_
V8_INLINE constexpr bool IsExternalString(InstanceType instance_type)
WasmImportWrapperCache * GetWasmImportWrapperCache()
WasmCodeManager * GetWasmCodeManager()
WasmEngine * GetWasmEngine()
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
Tagged< MaybeWeak< T > > MakeWeak(Tagged< T > value)
Definition tagged.h:893
V8_EXPORT_PRIVATE FlagValues v8_flags
std::unique_ptr< CancelableTask > MakeCancelableTask(Isolate *isolate, std::function< void()> func)
Definition task-utils.cc:43
tsan_relaxed_store_8_bits tsan_relaxed_store_32_bits tsan_seq_cst_store_8_bits tsan_seq_cst_store_32_bits tsan_relaxed_load_32_bits Address raw_context
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Local< T > Handle
v8::Local< T > ToApiHandle(v8::internal::DirectHandle< v8::internal::Object > obj)
Definition api.h:297
MeasureMemoryExecution
MeasureMemoryMode
Definition c-api.cc:87
#define CHECK(condition)
Definition logging.h:124
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_EXPORT_PRIVATE
Definition macros.h:460
std::unique_ptr< v8::MeasureMemoryDelegate > delegate