blob: c061e81433cd57f2e33429f76532e1d4613e94cf [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"
Hans Wennborg5cd6d192020-06-18 11:14:5611#include "base/logging.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:
138 LOG(FATAL) << "Unreachable code";
139 return "test";
140 case IsolateHolder::IsolateType::kUtility:
141 return "utility";
142 }
143 LOG(FATAL) << "Unreachable code";
144}
145
146bool CanHaveMultipleIsolates(IsolateHolder::IsolateType isolate_type) {
147 switch (isolate_type) {
148 case IsolateHolder::IsolateType::kBlinkMainThread:
149 return false;
150 case IsolateHolder::IsolateType::kBlinkWorkerThread:
151 return true;
152 case IsolateHolder::IsolateType::kTest:
153 LOG(FATAL) << "Unreachable code";
154 return false;
155 case IsolateHolder::IsolateType::kUtility:
156 // PDFium and ProxyResolver create one isolate per process.
157 return false;
158 }
159 LOG(FATAL) << "Unreachable code";
160}
161
Corentin Pescheloche959ce8b2021-11-08 21:49:47162} // namespace
mythria35540662016-06-03 15:02:05163
sside36cfaf2015-06-12 16:42:20164void V8IsolateMemoryDumpProvider::DumpHeapStatistics(
ssid2888a242015-08-07 23:08:42165 const base::trace_event::MemoryDumpArgs& args,
sside36cfaf2015-06-12 16:42:20166 base::trace_event::ProcessMemoryDump* process_memory_dump) {
Ho Cheungadbf3fb2023-09-08 02:01:11167 if (args.determinism == base::trace_event::MemoryDumpDeterminism::kForceGc) {
Ulan Degenbaev4f2f1a12019-09-24 14:42:57168 // Force GC in V8 using the same API as DevTools uses in "collectGarbage".
169 isolate_holder_->isolate()->LowMemoryNotification();
170 }
Ulan Degenbaev1d316332018-09-03 14:19:08171 std::string isolate_name = base::StringPrintf(
172 "isolate_0x%" PRIXPTR,
ssid4205d7b2016-06-15 20:36:03173 reinterpret_cast<uintptr_t>(isolate_holder_->isolate()));
sside36cfaf2015-06-12 16:42:20174
Dominik Inführ524999c32022-08-25 20:01:47175 // Dump statistics of the heap's spaces.
176 v8::HeapStatistics heap_statistics;
177 // The total heap sizes should be sampled before the individual space sizes
178 // because of concurrent allocation. DCHECKs below rely on this order.
179 isolate_holder_->isolate()->GetHeapStatistics(&heap_statistics);
180
Ulan Degenbaev1d316332018-09-03 14:19:08181 IsolateHolder::IsolateType isolate_type = isolate_holder_->isolate_type();
182 std::string dump_base_name = "v8/" + IsolateTypeString(isolate_type);
183 std::string dump_name_suffix =
184 CanHaveMultipleIsolates(isolate_type) ? "/" + isolate_name : "";
185
186 std::string space_name_prefix = dump_base_name + "/heap";
187
ssid83aa5be2015-05-08 12:03:26188 size_t known_spaces_size = 0;
primiano553bea9a2015-09-09 16:37:35189 size_t known_spaces_physical_size = 0;
ssid83aa5be2015-05-08 12:03:26190 size_t number_of_spaces = isolate_holder_->isolate()->NumberOfHeapSpaces();
191 for (size_t space = 0; space < number_of_spaces; space++) {
192 v8::HeapSpaceStatistics space_statistics;
193 isolate_holder_->isolate()->GetHeapSpaceStatistics(&space_statistics,
194 space);
195 const size_t space_size = space_statistics.space_size();
196 const size_t space_used_size = space_statistics.space_used_size();
primiano553bea9a2015-09-09 16:37:35197 const size_t space_physical_size = space_statistics.physical_space_size();
ssid83aa5be2015-05-08 12:03:26198
199 known_spaces_size += space_size;
primiano553bea9a2015-09-09 16:37:35200 known_spaces_physical_size += space_physical_size;
ssid83aa5be2015-05-08 12:03:26201
Ulan Degenbaev1d316332018-09-03 14:19:08202 std::string space_dump_name = dump_base_name + "/heap/" +
203 space_statistics.space_name() +
204 dump_name_suffix;
205
vmpstr1ee03c4c02016-06-30 21:40:56206 auto* space_dump =
207 process_memory_dump->CreateAllocatorDump(space_dump_name);
sside36cfaf2015-06-12 16:42:20208 space_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
ssid83aa5be2015-05-08 12:03:26209 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
primiano553bea9a2015-09-09 16:37:35210 space_physical_size);
primiano553bea9a2015-09-09 16:37:35211 space_dump->AddScalar("virtual_size",
212 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
sside36cfaf2015-06-12 16:42:20213 space_size);
214
primiano553bea9a2015-09-09 16:37:35215 space_dump->AddScalar("allocated_objects_size",
216 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
217 space_used_size);
ssid83aa5be2015-05-08 12:03:26218 }
ssidf51216b02015-06-04 19:46:23219
Dominik Inführfc969142022-08-30 08:25:37220 // Sanity check that all spaces are accounted for in GetHeapSpaceStatistics.
Ulan Degenbaev9d6e209b2020-09-03 09:12:30221 // Background threads may be running and allocating concurrently, so the sum
Dominik Inführfc969142022-08-30 08:25:37222 // of space sizes may exceed the total heap size that was sampled earlier.
Ulan Degenbaev9d6e209b2020-09-03 09:12:30223 DCHECK_LE(heap_statistics.total_heap_size(), known_spaces_size);
primiano553bea9a2015-09-09 16:37:35224
ssidbce6ee42015-11-05 02:35:39225 // If V8 zaps garbage, all the memory mapped regions become resident,
226 // so we add an extra dump to avoid mismatches w.r.t. the total
227 // resident values.
228 if (heap_statistics.does_zap_garbage()) {
vmpstr1ee03c4c02016-06-30 21:40:56229 auto* zap_dump = process_memory_dump->CreateAllocatorDump(
Ulan Degenbaev1d316332018-09-03 14:19:08230 dump_base_name + "/zapped_for_debug" + dump_name_suffix);
Dominik Inführfc969142022-08-30 08:25:37231 size_t zapped_size_for_debugging =
232 known_spaces_size >= known_spaces_physical_size
233 ? known_spaces_size - known_spaces_physical_size
234 : 0;
ssidbce6ee42015-11-05 02:35:39235 zap_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
236 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
Dominik Inführfc969142022-08-30 08:25:37237 zapped_size_for_debugging);
ssidbce6ee42015-11-05 02:35:39238 }
239
jochen07eb75792016-04-09 20:34:02240 // Dump statistics about malloced memory.
Ulan Degenbaev1d316332018-09-03 14:19:08241 std::string malloc_name = dump_base_name + "/malloc" + dump_name_suffix;
vmpstr1ee03c4c02016-06-30 21:40:56242 auto* malloc_dump = process_memory_dump->CreateAllocatorDump(malloc_name);
jochen07eb75792016-04-09 20:34:02243 malloc_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
244 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
245 heap_statistics.malloced_memory());
jochen077e9d72016-07-19 19:22:38246 malloc_dump->AddScalar("peak_size",
247 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
248 heap_statistics.peak_malloced_memory());
jochen07eb75792016-04-09 20:34:02249 const char* system_allocator_name =
250 base::trace_event::MemoryDumpManager::GetInstance()
251 ->system_allocator_pool_name();
252 if (system_allocator_name) {
253 process_memory_dump->AddSuballocation(malloc_dump->guid(),
254 system_allocator_name);
255 }
256
Ulan Degenbaev1d316332018-09-03 14:19:08257 DumpContextStatistics(process_memory_dump, dump_base_name, dump_name_suffix,
Ulan Degenbaevbfe58712017-12-15 19:40:54258 heap_statistics.number_of_detached_contexts(),
259 heap_statistics.number_of_native_contexts());
260
Ulan Degenbaev1d316332018-09-03 14:19:08261 auto* code_stats_dump = process_memory_dump->CreateAllocatorDump(
262 dump_base_name + "/code_stats" + dump_name_suffix);
mythria35540662016-06-03 15:02:05263
rmcilroy6b6b89a2016-08-25 15:12:57264 // Dump statistics related to code and bytecode if requested.
Ulan Degenbaev1d316332018-09-03 14:19:08265 DumpCodeStatistics(code_stats_dump, isolate_holder_);
rmcilroy6b6b89a2016-08-25 15:12:57266
Dominik Inführe35b6412020-03-09 21:32:56267 // Dump statistics for global handles.
268 auto* global_handles_dump = process_memory_dump->CreateAllocatorDump(
269 dump_base_name + "/global_handles" + dump_name_suffix);
270 global_handles_dump->AddScalar(
271 base::trace_event::MemoryAllocatorDump::kNameSize,
272 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
273 heap_statistics.total_global_handles_size());
Dominik Inführaf9fe22e2021-02-05 14:15:46274 global_handles_dump->AddScalar(
275 "allocated_objects_size",
276 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
277 heap_statistics.used_global_handles_size());
278 if (system_allocator_name) {
279 process_memory_dump->AddSuballocation(global_handles_dump->guid(),
280 system_allocator_name);
281 }
Dominik Inführe35b6412020-03-09 21:32:56282
ssid3011f202016-06-01 20:24:26283 // Dump object statistics only for detailed dumps.
284 if (args.level_of_detail !=
Ho Cheungadbf3fb2023-09-08 02:01:11285 base::trace_event::MemoryDumpLevelOfDetail::kDetailed) {
ssid2888a242015-08-07 23:08:42286 return;
ssid3011f202016-06-01 20:24:26287 }
ssid2888a242015-08-07 23:08:42288
sside36cfaf2015-06-12 16:42:20289 // Dump statistics of the heap's live objects from last GC.
primiano553bea9a2015-09-09 16:37:35290 // TODO(primiano): these should not be tracked in the same trace event as they
291 // report stats for the last GC (not the current state). See crbug.com/498779.
Ulan Degenbaev1d316332018-09-03 14:19:08292 std::string object_name_prefix =
293 dump_base_name + "/heap_objects_at_last_gc" + dump_name_suffix;
sside36cfaf2015-06-12 16:42:20294 bool did_dump_object_stats = false;
ssidf51216b02015-06-04 19:46:23295 const size_t object_types =
296 isolate_holder_->isolate()->NumberOfTrackedHeapObjectTypes();
297 for (size_t type_index = 0; type_index < object_types; type_index++) {
298 v8::HeapObjectStatistics object_statistics;
299 if (!isolate_holder_->isolate()->GetHeapObjectStatisticsAtLastGC(
300 &object_statistics, type_index))
301 continue;
302
303 std::string dump_name =
sside36cfaf2015-06-12 16:42:20304 object_name_prefix + "/" + object_statistics.object_type();
ssidf51216b02015-06-04 19:46:23305 if (object_statistics.object_sub_type()[0] != '\0')
306 dump_name += std::string("/") + object_statistics.object_sub_type();
vmpstr1ee03c4c02016-06-30 21:40:56307 auto* object_dump = process_memory_dump->CreateAllocatorDump(dump_name);
ssidf51216b02015-06-04 19:46:23308
309 object_dump->AddScalar(
bratell10f2e3c2015-09-10 16:28:43310 base::trace_event::MemoryAllocatorDump::kNameObjectCount,
ssidf51216b02015-06-04 19:46:23311 base::trace_event::MemoryAllocatorDump::kUnitsObjects,
312 object_statistics.object_count());
sside36cfaf2015-06-12 16:42:20313 object_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
314 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
315 object_statistics.object_size());
316 did_dump_object_stats = true;
ssidf51216b02015-06-04 19:46:23317 }
318
sside36cfaf2015-06-12 16:42:20319 if (process_memory_dump->GetAllocatorDump(object_name_prefix +
320 "/CODE_TYPE")) {
vmpstr1ee03c4c02016-06-30 21:40:56321 auto* code_kind_dump = process_memory_dump->CreateAllocatorDump(
sside36cfaf2015-06-12 16:42:20322 object_name_prefix + "/CODE_TYPE/CODE_KIND");
vmpstr1ee03c4c02016-06-30 21:40:56323 auto* code_age_dump = process_memory_dump->CreateAllocatorDump(
sside36cfaf2015-06-12 16:42:20324 object_name_prefix + "/CODE_TYPE/CODE_AGE");
325 process_memory_dump->AddOwnershipEdge(code_kind_dump->guid(),
326 code_age_dump->guid());
327 }
328
329 if (did_dump_object_stats) {
330 process_memory_dump->AddOwnershipEdge(
331 process_memory_dump->CreateAllocatorDump(object_name_prefix)->guid(),
Ulan Degenbaev1d316332018-09-03 14:19:08332 process_memory_dump->GetOrCreateAllocatorDump(space_name_prefix)
333 ->guid());
sside36cfaf2015-06-12 16:42:20334 }
ssidf51216b02015-06-04 19:46:23335}
336
ssid83aa5be2015-05-08 12:03:26337} // namespace gin