blob: e7690fe4085fd8355df7e852893e7d6e3d769f66 [file] [log] [blame]
Avi Drissman468e51b62022-09-13 20:47:011// Copyright 2015 The Chromium Authors
ssid83aa5be2015-05-08 12:03:262// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "gin/v8_isolate_memory_dump_provider.h"
6
ssid4205d7b2016-06-15 20:36:037#include <inttypes.h>
avi90e658dd2015-12-21 07:16:198#include <stddef.h>
9
Lei Zhang8815c182022-11-03 14:40:2710#include "base/check_op.h"
Peter Boströmf5d51552024-01-13 05:46:2911#include "base/notreached.h"
ssid83aa5be2015-05-08 12:03:2612#include "base/strings/stringprintf.h"
Sean Mahere672a662023-01-09 21:42:2813#include "base/task/single_thread_task_runner.h"
ssid83aa5be2015-05-08 12:03:2614#include "base/trace_event/memory_dump_manager.h"
15#include "base/trace_event/process_memory_dump.h"
16#include "gin/public/isolate_holder.h"
Dan Elphick05acd602021-08-30 15:22:0717#include "v8/include/v8-isolate.h"
18#include "v8/include/v8-locker.h"
19#include "v8/include/v8-statistics.h"
ssid83aa5be2015-05-08 12:03:2620
21namespace gin {
22
ssid83aa5be2015-05-08 12:03:2623V8IsolateMemoryDumpProvider::V8IsolateMemoryDumpProvider(
Siddhartha331b5f0d2017-12-12 14:47:0524 IsolateHolder* isolate_holder,
25 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
ssid83aa5be2015-05-08 12:03:2626 : isolate_holder_(isolate_holder) {
Siddhartha331b5f0d2017-12-12 14:47:0527 DCHECK(task_runner);
ssid83aa5be2015-05-08 12:03:2628 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
Siddhartha331b5f0d2017-12-12 14:47:0529 this, "V8Isolate", task_runner);
ssid83aa5be2015-05-08 12:03:2630}
31
32V8IsolateMemoryDumpProvider::~V8IsolateMemoryDumpProvider() {
33 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
34 this);
35}
36
37// Called at trace dump point time. Creates a snapshot with the memory counters
38// for the current isolate.
39bool V8IsolateMemoryDumpProvider::OnMemoryDump(
ssid90694aeec2015-08-06 13:01:3040 const base::trace_event::MemoryDumpArgs& args,
ssidf51216b02015-06-04 19:46:2341 base::trace_event::ProcessMemoryDump* process_memory_dump) {
ssid90694aeec2015-08-06 13:01:3042 // TODO(ssid): Use MemoryDumpArgs to create light dumps when requested
43 // (crbug.com/499731).
44
ssid83aa5be2015-05-08 12:03:2645 if (isolate_holder_->access_mode() == IsolateHolder::kUseLocker) {
46 v8::Locker locked(isolate_holder_->isolate());
ssid2888a242015-08-07 23:08:4247 DumpHeapStatistics(args, process_memory_dump);
ssid83aa5be2015-05-08 12:03:2648 } else {
ssid2888a242015-08-07 23:08:4249 DumpHeapStatistics(args, process_memory_dump);
ssid83aa5be2015-05-08 12:03:2650 }
51 return true;
52}
53
mythria35540662016-06-03 15:02:0554namespace {
55
56// Dump statistics related to code/bytecode when memory-infra.v8.code_stats is
57// enabled.
Ulan Degenbaev1d316332018-09-03 14:19:0858void DumpCodeStatistics(base::trace_event::MemoryAllocatorDump* dump,
59 IsolateHolder* isolate_holder) {
mythria35540662016-06-03 15:02:0560 // Collecting code statistics is an expensive operation (~10 ms) when
61 // compared to other v8 metrics (< 1 ms). So, dump them only when
62 // memory-infra.v8.code_stats is enabled.
63 // TODO(primiano): This information should be plumbed through TraceConfig.
64 // See crbug.com/616441.
65 bool dump_code_stats = false;
66 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
67 TRACE_DISABLED_BY_DEFAULT("memory-infra.v8.code_stats"),
68 &dump_code_stats);
69 if (!dump_code_stats)
70 return;
71
72 v8::HeapCodeStatistics code_statistics;
73 if (!isolate_holder->isolate()->GetHeapCodeAndMetadataStatistics(
74 &code_statistics)) {
75 return;
76 }
77
Ulan Degenbaev1d316332018-09-03 14:19:0878 dump->AddScalar("code_and_metadata_size",
79 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
80 code_statistics.code_and_metadata_size());
81 dump->AddScalar("bytecode_and_metadata_size",
82 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
83 code_statistics.bytecode_and_metadata_size());
84 dump->AddScalar("external_script_source_size",
85 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
86 code_statistics.external_script_source_size());
Corentin Pescheloche959ce8b2021-11-08 21:49:4787 dump->AddScalar("cpu_profiler_metadata_size",
88 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
89 code_statistics.cpu_profiler_metadata_size());
mythria35540662016-06-03 15:02:0590}
91
Ulan Degenbaevbfe58712017-12-15 19:40:5492// Dump the number of native and detached contexts.
93// The result looks as follows in the Chrome trace viewer:
Ulan Degenbaev1d316332018-09-03 14:19:0894// ========================================
95// Component object_count
Ulan Degenbaevbfe58712017-12-15 19:40:5496// - v8
Ulan Degenbaev1d316332018-09-03 14:19:0897// - main
Ulan Degenbaevbfe58712017-12-15 19:40:5498// - contexts
99// - detached_context 10
100// - native_context 20
Ulan Degenbaevdc12d16a2018-09-06 15:12:16101// - workers
102// - contexts
103// - detached_context
104// - isolate_0x1234 10
105// - native_context
106// - isolate_0x1234 20
Ulan Degenbaev1d316332018-09-03 14:19:08107// ========================================
Ulan Degenbaevbfe58712017-12-15 19:40:54108void DumpContextStatistics(
109 base::trace_event::ProcessMemoryDump* process_memory_dump,
110 std::string dump_base_name,
Ulan Degenbaev1d316332018-09-03 14:19:08111 std::string dump_name_suffix,
Ulan Degenbaevbfe58712017-12-15 19:40:54112 size_t number_of_detached_contexts,
113 size_t number_of_native_contexts) {
Ulan Degenbaevdc12d16a2018-09-06 15:12:16114 std::string dump_name_prefix = dump_base_name + "/contexts";
Ulan Degenbaev1d316332018-09-03 14:19:08115 std::string native_context_name =
116 dump_name_prefix + "/native_context" + dump_name_suffix;
Ulan Degenbaevbfe58712017-12-15 19:40:54117 auto* native_context_dump =
118 process_memory_dump->CreateAllocatorDump(native_context_name);
119 native_context_dump->AddScalar(
120 "object_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects,
121 number_of_native_contexts);
Ulan Degenbaevdc12d16a2018-09-06 15:12:16122 std::string detached_context_name =
123 dump_name_prefix + "/detached_context" + dump_name_suffix;
Ulan Degenbaevbfe58712017-12-15 19:40:54124 auto* detached_context_dump =
125 process_memory_dump->CreateAllocatorDump(detached_context_name);
126 detached_context_dump->AddScalar(
127 "object_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects,
128 number_of_detached_contexts);
129}
130
Ulan Degenbaev1d316332018-09-03 14:19:08131std::string IsolateTypeString(IsolateHolder::IsolateType isolate_type) {
132 switch (isolate_type) {
133 case IsolateHolder::IsolateType::kBlinkMainThread:
134 return "main";
135 case IsolateHolder::IsolateType::kBlinkWorkerThread:
136 return "workers";
137 case IsolateHolder::IsolateType::kTest:
Peter Boström01ab59a2024-08-15 02:39:49138 NOTREACHED();
Ulan Degenbaev1d316332018-09-03 14:19:08139 case IsolateHolder::IsolateType::kUtility:
140 return "utility";
141 }
Peter Boström01ab59a2024-08-15 02:39:49142 NOTREACHED();
Ulan Degenbaev1d316332018-09-03 14:19:08143}
144
145bool CanHaveMultipleIsolates(IsolateHolder::IsolateType isolate_type) {
146 switch (isolate_type) {
147 case IsolateHolder::IsolateType::kBlinkMainThread:
148 return false;
149 case IsolateHolder::IsolateType::kBlinkWorkerThread:
150 return true;
151 case IsolateHolder::IsolateType::kTest:
Peter Boström01ab59a2024-08-15 02:39:49152 NOTREACHED();
Ulan Degenbaev1d316332018-09-03 14:19:08153 case IsolateHolder::IsolateType::kUtility:
154 // PDFium and ProxyResolver create one isolate per process.
155 return false;
156 }
Peter Boström01ab59a2024-08-15 02:39:49157 NOTREACHED();
Ulan Degenbaev1d316332018-09-03 14:19:08158}
159
Corentin Pescheloche959ce8b2021-11-08 21:49:47160} // namespace
mythria35540662016-06-03 15:02:05161
sside36cfaf2015-06-12 16:42:20162void V8IsolateMemoryDumpProvider::DumpHeapStatistics(
ssid2888a242015-08-07 23:08:42163 const base::trace_event::MemoryDumpArgs& args,
sside36cfaf2015-06-12 16:42:20164 base::trace_event::ProcessMemoryDump* process_memory_dump) {
Ho Cheungadbf3fb2023-09-08 02:01:11165 if (args.determinism == base::trace_event::MemoryDumpDeterminism::kForceGc) {
Ulan Degenbaev4f2f1a12019-09-24 14:42:57166 // Force GC in V8 using the same API as DevTools uses in "collectGarbage".
167 isolate_holder_->isolate()->LowMemoryNotification();
168 }
Ulan Degenbaev1d316332018-09-03 14:19:08169 std::string isolate_name = base::StringPrintf(
170 "isolate_0x%" PRIXPTR,
ssid4205d7b2016-06-15 20:36:03171 reinterpret_cast<uintptr_t>(isolate_holder_->isolate()));
sside36cfaf2015-06-12 16:42:20172
Dominik Inführ524999c32022-08-25 20:01:47173 // Dump statistics of the heap's spaces.
174 v8::HeapStatistics heap_statistics;
175 // The total heap sizes should be sampled before the individual space sizes
176 // because of concurrent allocation. DCHECKs below rely on this order.
177 isolate_holder_->isolate()->GetHeapStatistics(&heap_statistics);
178
Ulan Degenbaev1d316332018-09-03 14:19:08179 IsolateHolder::IsolateType isolate_type = isolate_holder_->isolate_type();
180 std::string dump_base_name = "v8/" + IsolateTypeString(isolate_type);
181 std::string dump_name_suffix =
182 CanHaveMultipleIsolates(isolate_type) ? "/" + isolate_name : "";
183
184 std::string space_name_prefix = dump_base_name + "/heap";
185
ssid83aa5be2015-05-08 12:03:26186 size_t known_spaces_size = 0;
primiano553bea9a2015-09-09 16:37:35187 size_t known_spaces_physical_size = 0;
ssid83aa5be2015-05-08 12:03:26188 size_t number_of_spaces = isolate_holder_->isolate()->NumberOfHeapSpaces();
189 for (size_t space = 0; space < number_of_spaces; space++) {
190 v8::HeapSpaceStatistics space_statistics;
191 isolate_holder_->isolate()->GetHeapSpaceStatistics(&space_statistics,
192 space);
193 const size_t space_size = space_statistics.space_size();
194 const size_t space_used_size = space_statistics.space_used_size();
primiano553bea9a2015-09-09 16:37:35195 const size_t space_physical_size = space_statistics.physical_space_size();
ssid83aa5be2015-05-08 12:03:26196
197 known_spaces_size += space_size;
primiano553bea9a2015-09-09 16:37:35198 known_spaces_physical_size += space_physical_size;
ssid83aa5be2015-05-08 12:03:26199
Ulan Degenbaev1d316332018-09-03 14:19:08200 std::string space_dump_name = dump_base_name + "/heap/" +
201 space_statistics.space_name() +
202 dump_name_suffix;
203
vmpstr1ee03c4c02016-06-30 21:40:56204 auto* space_dump =
205 process_memory_dump->CreateAllocatorDump(space_dump_name);
sside36cfaf2015-06-12 16:42:20206 space_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
ssid83aa5be2015-05-08 12:03:26207 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
primiano553bea9a2015-09-09 16:37:35208 space_physical_size);
primiano553bea9a2015-09-09 16:37:35209 space_dump->AddScalar("virtual_size",
210 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
sside36cfaf2015-06-12 16:42:20211 space_size);
212
primiano553bea9a2015-09-09 16:37:35213 space_dump->AddScalar("allocated_objects_size",
214 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
215 space_used_size);
ssid83aa5be2015-05-08 12:03:26216 }
ssidf51216b02015-06-04 19:46:23217
Dominik Inführfc969142022-08-30 08:25:37218 // Sanity check that all spaces are accounted for in GetHeapSpaceStatistics.
Ulan Degenbaev9d6e209b2020-09-03 09:12:30219 // Background threads may be running and allocating concurrently, so the sum
Dominik Inführfc969142022-08-30 08:25:37220 // of space sizes may exceed the total heap size that was sampled earlier.
Ulan Degenbaev9d6e209b2020-09-03 09:12:30221 DCHECK_LE(heap_statistics.total_heap_size(), known_spaces_size);
primiano553bea9a2015-09-09 16:37:35222
ssidbce6ee42015-11-05 02:35:39223 // If V8 zaps garbage, all the memory mapped regions become resident,
224 // so we add an extra dump to avoid mismatches w.r.t. the total
225 // resident values.
226 if (heap_statistics.does_zap_garbage()) {
vmpstr1ee03c4c02016-06-30 21:40:56227 auto* zap_dump = process_memory_dump->CreateAllocatorDump(
Ulan Degenbaev1d316332018-09-03 14:19:08228 dump_base_name + "/zapped_for_debug" + dump_name_suffix);
Dominik Inführfc969142022-08-30 08:25:37229 size_t zapped_size_for_debugging =
230 known_spaces_size >= known_spaces_physical_size
231 ? known_spaces_size - known_spaces_physical_size
232 : 0;
ssidbce6ee42015-11-05 02:35:39233 zap_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
234 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
Dominik Inführfc969142022-08-30 08:25:37235 zapped_size_for_debugging);
ssidbce6ee42015-11-05 02:35:39236 }
237
jochen07eb75792016-04-09 20:34:02238 // Dump statistics about malloced memory.
Ulan Degenbaev1d316332018-09-03 14:19:08239 std::string malloc_name = dump_base_name + "/malloc" + dump_name_suffix;
vmpstr1ee03c4c02016-06-30 21:40:56240 auto* malloc_dump = process_memory_dump->CreateAllocatorDump(malloc_name);
jochen07eb75792016-04-09 20:34:02241 malloc_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
242 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
243 heap_statistics.malloced_memory());
jochen077e9d72016-07-19 19:22:38244 malloc_dump->AddScalar("peak_size",
245 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
246 heap_statistics.peak_malloced_memory());
jochen07eb75792016-04-09 20:34:02247 const char* system_allocator_name =
248 base::trace_event::MemoryDumpManager::GetInstance()
249 ->system_allocator_pool_name();
250 if (system_allocator_name) {
251 process_memory_dump->AddSuballocation(malloc_dump->guid(),
252 system_allocator_name);
253 }
254
Ulan Degenbaev1d316332018-09-03 14:19:08255 DumpContextStatistics(process_memory_dump, dump_base_name, dump_name_suffix,
Ulan Degenbaevbfe58712017-12-15 19:40:54256 heap_statistics.number_of_detached_contexts(),
257 heap_statistics.number_of_native_contexts());
258
Ulan Degenbaev1d316332018-09-03 14:19:08259 auto* code_stats_dump = process_memory_dump->CreateAllocatorDump(
260 dump_base_name + "/code_stats" + dump_name_suffix);
mythria35540662016-06-03 15:02:05261
rmcilroy6b6b89a2016-08-25 15:12:57262 // Dump statistics related to code and bytecode if requested.
Ulan Degenbaev1d316332018-09-03 14:19:08263 DumpCodeStatistics(code_stats_dump, isolate_holder_);
rmcilroy6b6b89a2016-08-25 15:12:57264
Dominik Inführe35b6412020-03-09 21:32:56265 // Dump statistics for global handles.
266 auto* global_handles_dump = process_memory_dump->CreateAllocatorDump(
267 dump_base_name + "/global_handles" + dump_name_suffix);
268 global_handles_dump->AddScalar(
269 base::trace_event::MemoryAllocatorDump::kNameSize,
270 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
271 heap_statistics.total_global_handles_size());
Dominik Inführaf9fe22e2021-02-05 14:15:46272 global_handles_dump->AddScalar(
273 "allocated_objects_size",
274 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
275 heap_statistics.used_global_handles_size());
276 if (system_allocator_name) {
277 process_memory_dump->AddSuballocation(global_handles_dump->guid(),
278 system_allocator_name);
279 }
Dominik Inführe35b6412020-03-09 21:32:56280
ssid3011f202016-06-01 20:24:26281 // Dump object statistics only for detailed dumps.
282 if (args.level_of_detail !=
Ho Cheungadbf3fb2023-09-08 02:01:11283 base::trace_event::MemoryDumpLevelOfDetail::kDetailed) {
ssid2888a242015-08-07 23:08:42284 return;
ssid3011f202016-06-01 20:24:26285 }
ssid2888a242015-08-07 23:08:42286
sside36cfaf2015-06-12 16:42:20287 // Dump statistics of the heap's live objects from last GC.
primiano553bea9a2015-09-09 16:37:35288 // TODO(primiano): these should not be tracked in the same trace event as they
289 // report stats for the last GC (not the current state). See crbug.com/498779.
Ulan Degenbaev1d316332018-09-03 14:19:08290 std::string object_name_prefix =
291 dump_base_name + "/heap_objects_at_last_gc" + dump_name_suffix;
sside36cfaf2015-06-12 16:42:20292 bool did_dump_object_stats = false;
ssidf51216b02015-06-04 19:46:23293 const size_t object_types =
294 isolate_holder_->isolate()->NumberOfTrackedHeapObjectTypes();
295 for (size_t type_index = 0; type_index < object_types; type_index++) {
296 v8::HeapObjectStatistics object_statistics;
297 if (!isolate_holder_->isolate()->GetHeapObjectStatisticsAtLastGC(
298 &object_statistics, type_index))
299 continue;
300
301 std::string dump_name =
sside36cfaf2015-06-12 16:42:20302 object_name_prefix + "/" + object_statistics.object_type();
ssidf51216b02015-06-04 19:46:23303 if (object_statistics.object_sub_type()[0] != '\0')
304 dump_name += std::string("/") + object_statistics.object_sub_type();
vmpstr1ee03c4c02016-06-30 21:40:56305 auto* object_dump = process_memory_dump->CreateAllocatorDump(dump_name);
ssidf51216b02015-06-04 19:46:23306
307 object_dump->AddScalar(
bratell10f2e3c2015-09-10 16:28:43308 base::trace_event::MemoryAllocatorDump::kNameObjectCount,
ssidf51216b02015-06-04 19:46:23309 base::trace_event::MemoryAllocatorDump::kUnitsObjects,
310 object_statistics.object_count());
sside36cfaf2015-06-12 16:42:20311 object_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
312 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
313 object_statistics.object_size());
314 did_dump_object_stats = true;
ssidf51216b02015-06-04 19:46:23315 }
316
sside36cfaf2015-06-12 16:42:20317 if (process_memory_dump->GetAllocatorDump(object_name_prefix +
318 "/CODE_TYPE")) {
vmpstr1ee03c4c02016-06-30 21:40:56319 auto* code_kind_dump = process_memory_dump->CreateAllocatorDump(
sside36cfaf2015-06-12 16:42:20320 object_name_prefix + "/CODE_TYPE/CODE_KIND");
vmpstr1ee03c4c02016-06-30 21:40:56321 auto* code_age_dump = process_memory_dump->CreateAllocatorDump(
sside36cfaf2015-06-12 16:42:20322 object_name_prefix + "/CODE_TYPE/CODE_AGE");
323 process_memory_dump->AddOwnershipEdge(code_kind_dump->guid(),
324 code_age_dump->guid());
325 }
326
327 if (did_dump_object_stats) {
328 process_memory_dump->AddOwnershipEdge(
329 process_memory_dump->CreateAllocatorDump(object_name_prefix)->guid(),
Ulan Degenbaev1d316332018-09-03 14:19:08330 process_memory_dump->GetOrCreateAllocatorDump(space_name_prefix)
331 ->guid());
sside36cfaf2015-06-12 16:42:20332 }
ssidf51216b02015-06-04 19:46:23333}
334
ssid83aa5be2015-05-08 12:03:26335} // namespace gin