blob: 4e328f11573bd6216c0f9e495cfee7705047c2fd [file] [log] [blame]
ssid83aa5be2015-05-08 12:03:261// Copyright 2015 The Chromium 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
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
ssid83aa5be2015-05-08 12:03:2610#include "base/strings/stringprintf.h"
ssid83aa5be2015-05-08 12:03:2611#include "base/trace_event/memory_dump_manager.h"
12#include "base/trace_event/process_memory_dump.h"
13#include "gin/public/isolate_holder.h"
14#include "v8/include/v8.h"
15
16namespace gin {
17
ssid83aa5be2015-05-08 12:03:2618V8IsolateMemoryDumpProvider::V8IsolateMemoryDumpProvider(
Siddhartha331b5f0d2017-12-12 14:47:0519 IsolateHolder* isolate_holder,
20 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
ssid83aa5be2015-05-08 12:03:2621 : isolate_holder_(isolate_holder) {
Siddhartha331b5f0d2017-12-12 14:47:0522 DCHECK(task_runner);
ssid83aa5be2015-05-08 12:03:2623 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
Siddhartha331b5f0d2017-12-12 14:47:0524 this, "V8Isolate", task_runner);
ssid83aa5be2015-05-08 12:03:2625}
26
27V8IsolateMemoryDumpProvider::~V8IsolateMemoryDumpProvider() {
28 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
29 this);
30}
31
32// Called at trace dump point time. Creates a snapshot with the memory counters
33// for the current isolate.
34bool V8IsolateMemoryDumpProvider::OnMemoryDump(
ssid90694aeec2015-08-06 13:01:3035 const base::trace_event::MemoryDumpArgs& args,
ssidf51216b02015-06-04 19:46:2336 base::trace_event::ProcessMemoryDump* process_memory_dump) {
ssid90694aeec2015-08-06 13:01:3037 // TODO(ssid): Use MemoryDumpArgs to create light dumps when requested
38 // (crbug.com/499731).
39
ssid83aa5be2015-05-08 12:03:2640 if (isolate_holder_->access_mode() == IsolateHolder::kUseLocker) {
41 v8::Locker locked(isolate_holder_->isolate());
ssid2888a242015-08-07 23:08:4242 DumpHeapStatistics(args, process_memory_dump);
ssid83aa5be2015-05-08 12:03:2643 } else {
ssid2888a242015-08-07 23:08:4244 DumpHeapStatistics(args, process_memory_dump);
ssid83aa5be2015-05-08 12:03:2645 }
46 return true;
47}
48
mythria35540662016-06-03 15:02:0549namespace {
50
51// Dump statistics related to code/bytecode when memory-infra.v8.code_stats is
52// enabled.
Ulan Degenbaev1d316332018-09-03 14:19:0853void DumpCodeStatistics(base::trace_event::MemoryAllocatorDump* dump,
54 IsolateHolder* isolate_holder) {
mythria35540662016-06-03 15:02:0555 // Collecting code statistics is an expensive operation (~10 ms) when
56 // compared to other v8 metrics (< 1 ms). So, dump them only when
57 // memory-infra.v8.code_stats is enabled.
58 // TODO(primiano): This information should be plumbed through TraceConfig.
59 // See crbug.com/616441.
60 bool dump_code_stats = false;
61 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
62 TRACE_DISABLED_BY_DEFAULT("memory-infra.v8.code_stats"),
63 &dump_code_stats);
64 if (!dump_code_stats)
65 return;
66
67 v8::HeapCodeStatistics code_statistics;
68 if (!isolate_holder->isolate()->GetHeapCodeAndMetadataStatistics(
69 &code_statistics)) {
70 return;
71 }
72
Ulan Degenbaev1d316332018-09-03 14:19:0873 dump->AddScalar("code_and_metadata_size",
74 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
75 code_statistics.code_and_metadata_size());
76 dump->AddScalar("bytecode_and_metadata_size",
77 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
78 code_statistics.bytecode_and_metadata_size());
79 dump->AddScalar("external_script_source_size",
80 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
81 code_statistics.external_script_source_size());
mythria35540662016-06-03 15:02:0582}
83
Ulan Degenbaevbfe58712017-12-15 19:40:5484// Dump the number of native and detached contexts.
85// The result looks as follows in the Chrome trace viewer:
Ulan Degenbaev1d316332018-09-03 14:19:0886// ========================================
87// Component object_count
Ulan Degenbaevbfe58712017-12-15 19:40:5488// - v8
Ulan Degenbaev1d316332018-09-03 14:19:0889// - main
Ulan Degenbaevbfe58712017-12-15 19:40:5490// - contexts
91// - detached_context 10
92// - native_context 20
Ulan Degenbaevdc12d16a2018-09-06 15:12:1693// - workers
94// - contexts
95// - detached_context
96// - isolate_0x1234 10
97// - native_context
98// - isolate_0x1234 20
Ulan Degenbaev1d316332018-09-03 14:19:0899// ========================================
Ulan Degenbaevbfe58712017-12-15 19:40:54100void DumpContextStatistics(
101 base::trace_event::ProcessMemoryDump* process_memory_dump,
102 std::string dump_base_name,
Ulan Degenbaev1d316332018-09-03 14:19:08103 std::string dump_name_suffix,
Ulan Degenbaevbfe58712017-12-15 19:40:54104 size_t number_of_detached_contexts,
105 size_t number_of_native_contexts) {
Ulan Degenbaevdc12d16a2018-09-06 15:12:16106 std::string dump_name_prefix = dump_base_name + "/contexts";
Ulan Degenbaev1d316332018-09-03 14:19:08107 std::string native_context_name =
108 dump_name_prefix + "/native_context" + dump_name_suffix;
Ulan Degenbaevbfe58712017-12-15 19:40:54109 auto* native_context_dump =
110 process_memory_dump->CreateAllocatorDump(native_context_name);
111 native_context_dump->AddScalar(
112 "object_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects,
113 number_of_native_contexts);
Ulan Degenbaevdc12d16a2018-09-06 15:12:16114 std::string detached_context_name =
115 dump_name_prefix + "/detached_context" + dump_name_suffix;
Ulan Degenbaevbfe58712017-12-15 19:40:54116 auto* detached_context_dump =
117 process_memory_dump->CreateAllocatorDump(detached_context_name);
118 detached_context_dump->AddScalar(
119 "object_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects,
120 number_of_detached_contexts);
121}
122
Ulan Degenbaev1d316332018-09-03 14:19:08123std::string IsolateTypeString(IsolateHolder::IsolateType isolate_type) {
124 switch (isolate_type) {
125 case IsolateHolder::IsolateType::kBlinkMainThread:
126 return "main";
127 case IsolateHolder::IsolateType::kBlinkWorkerThread:
128 return "workers";
129 case IsolateHolder::IsolateType::kTest:
130 LOG(FATAL) << "Unreachable code";
131 return "test";
132 case IsolateHolder::IsolateType::kUtility:
133 return "utility";
134 }
135 LOG(FATAL) << "Unreachable code";
136}
137
138bool CanHaveMultipleIsolates(IsolateHolder::IsolateType isolate_type) {
139 switch (isolate_type) {
140 case IsolateHolder::IsolateType::kBlinkMainThread:
141 return false;
142 case IsolateHolder::IsolateType::kBlinkWorkerThread:
143 return true;
144 case IsolateHolder::IsolateType::kTest:
145 LOG(FATAL) << "Unreachable code";
146 return false;
147 case IsolateHolder::IsolateType::kUtility:
148 // PDFium and ProxyResolver create one isolate per process.
149 return false;
150 }
151 LOG(FATAL) << "Unreachable code";
152}
153
mythria35540662016-06-03 15:02:05154} // namespace anonymous
155
sside36cfaf2015-06-12 16:42:20156void V8IsolateMemoryDumpProvider::DumpHeapStatistics(
ssid2888a242015-08-07 23:08:42157 const base::trace_event::MemoryDumpArgs& args,
sside36cfaf2015-06-12 16:42:20158 base::trace_event::ProcessMemoryDump* process_memory_dump) {
Ulan Degenbaev4f2f1a12019-09-24 14:42:57159 if (args.determinism == base::trace_event::MemoryDumpDeterminism::FORCE_GC) {
160 // Force GC in V8 using the same API as DevTools uses in "collectGarbage".
161 isolate_holder_->isolate()->LowMemoryNotification();
162 }
Ulan Degenbaev1d316332018-09-03 14:19:08163 std::string isolate_name = base::StringPrintf(
164 "isolate_0x%" PRIXPTR,
ssid4205d7b2016-06-15 20:36:03165 reinterpret_cast<uintptr_t>(isolate_holder_->isolate()));
sside36cfaf2015-06-12 16:42:20166
167 // Dump statistics of the heap's spaces.
ssid83aa5be2015-05-08 12:03:26168 v8::HeapStatistics heap_statistics;
169 isolate_holder_->isolate()->GetHeapStatistics(&heap_statistics);
170
Ulan Degenbaev1d316332018-09-03 14:19:08171 IsolateHolder::IsolateType isolate_type = isolate_holder_->isolate_type();
172 std::string dump_base_name = "v8/" + IsolateTypeString(isolate_type);
173 std::string dump_name_suffix =
174 CanHaveMultipleIsolates(isolate_type) ? "/" + isolate_name : "";
175
176 std::string space_name_prefix = dump_base_name + "/heap";
177
ssid83aa5be2015-05-08 12:03:26178 size_t known_spaces_used_size = 0;
179 size_t known_spaces_size = 0;
primiano553bea9a2015-09-09 16:37:35180 size_t known_spaces_physical_size = 0;
ssid83aa5be2015-05-08 12:03:26181 size_t number_of_spaces = isolate_holder_->isolate()->NumberOfHeapSpaces();
182 for (size_t space = 0; space < number_of_spaces; space++) {
183 v8::HeapSpaceStatistics space_statistics;
184 isolate_holder_->isolate()->GetHeapSpaceStatistics(&space_statistics,
185 space);
186 const size_t space_size = space_statistics.space_size();
187 const size_t space_used_size = space_statistics.space_used_size();
primiano553bea9a2015-09-09 16:37:35188 const size_t space_physical_size = space_statistics.physical_space_size();
ssid83aa5be2015-05-08 12:03:26189
190 known_spaces_size += space_size;
191 known_spaces_used_size += space_used_size;
primiano553bea9a2015-09-09 16:37:35192 known_spaces_physical_size += space_physical_size;
ssid83aa5be2015-05-08 12:03:26193
Ulan Degenbaev1d316332018-09-03 14:19:08194 std::string space_dump_name = dump_base_name + "/heap/" +
195 space_statistics.space_name() +
196 dump_name_suffix;
197
vmpstr1ee03c4c02016-06-30 21:40:56198 auto* space_dump =
199 process_memory_dump->CreateAllocatorDump(space_dump_name);
sside36cfaf2015-06-12 16:42:20200 space_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
ssid83aa5be2015-05-08 12:03:26201 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
primiano553bea9a2015-09-09 16:37:35202 space_physical_size);
primiano553bea9a2015-09-09 16:37:35203 space_dump->AddScalar("virtual_size",
204 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
sside36cfaf2015-06-12 16:42:20205 space_size);
206
primiano553bea9a2015-09-09 16:37:35207 space_dump->AddScalar("allocated_objects_size",
208 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
209 space_used_size);
ssid83aa5be2015-05-08 12:03:26210 }
ssidf51216b02015-06-04 19:46:23211
Ulan Degenbaev46433ade2018-04-25 12:25:15212 // Sanity checks.
213 DCHECK_EQ(heap_statistics.total_physical_size(), known_spaces_physical_size);
214 DCHECK_EQ(heap_statistics.used_heap_size(), known_spaces_used_size);
215 DCHECK_EQ(heap_statistics.total_heap_size(), known_spaces_size);
primiano553bea9a2015-09-09 16:37:35216
ssidbce6ee42015-11-05 02:35:39217 // If V8 zaps garbage, all the memory mapped regions become resident,
218 // so we add an extra dump to avoid mismatches w.r.t. the total
219 // resident values.
220 if (heap_statistics.does_zap_garbage()) {
vmpstr1ee03c4c02016-06-30 21:40:56221 auto* zap_dump = process_memory_dump->CreateAllocatorDump(
Ulan Degenbaev1d316332018-09-03 14:19:08222 dump_base_name + "/zapped_for_debug" + dump_name_suffix);
ssidbce6ee42015-11-05 02:35:39223 zap_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
224 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
225 heap_statistics.total_heap_size() -
226 heap_statistics.total_physical_size());
227 }
228
jochen07eb75792016-04-09 20:34:02229 // Dump statistics about malloced memory.
Ulan Degenbaev1d316332018-09-03 14:19:08230 std::string malloc_name = dump_base_name + "/malloc" + dump_name_suffix;
vmpstr1ee03c4c02016-06-30 21:40:56231 auto* malloc_dump = process_memory_dump->CreateAllocatorDump(malloc_name);
jochen07eb75792016-04-09 20:34:02232 malloc_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
233 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
234 heap_statistics.malloced_memory());
jochen077e9d72016-07-19 19:22:38235 malloc_dump->AddScalar("peak_size",
236 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
237 heap_statistics.peak_malloced_memory());
jochen07eb75792016-04-09 20:34:02238 const char* system_allocator_name =
239 base::trace_event::MemoryDumpManager::GetInstance()
240 ->system_allocator_pool_name();
241 if (system_allocator_name) {
242 process_memory_dump->AddSuballocation(malloc_dump->guid(),
243 system_allocator_name);
244 }
245
Ulan Degenbaev1d316332018-09-03 14:19:08246 DumpContextStatistics(process_memory_dump, dump_base_name, dump_name_suffix,
Ulan Degenbaevbfe58712017-12-15 19:40:54247 heap_statistics.number_of_detached_contexts(),
248 heap_statistics.number_of_native_contexts());
249
Ulan Degenbaev1d316332018-09-03 14:19:08250 auto* code_stats_dump = process_memory_dump->CreateAllocatorDump(
251 dump_base_name + "/code_stats" + dump_name_suffix);
mythria35540662016-06-03 15:02:05252
rmcilroy6b6b89a2016-08-25 15:12:57253 // Dump statistics related to code and bytecode if requested.
Ulan Degenbaev1d316332018-09-03 14:19:08254 DumpCodeStatistics(code_stats_dump, isolate_holder_);
rmcilroy6b6b89a2016-08-25 15:12:57255
ssid3011f202016-06-01 20:24:26256 // Dump object statistics only for detailed dumps.
257 if (args.level_of_detail !=
258 base::trace_event::MemoryDumpLevelOfDetail::DETAILED) {
ssid2888a242015-08-07 23:08:42259 return;
ssid3011f202016-06-01 20:24:26260 }
ssid2888a242015-08-07 23:08:42261
sside36cfaf2015-06-12 16:42:20262 // Dump statistics of the heap's live objects from last GC.
primiano553bea9a2015-09-09 16:37:35263 // TODO(primiano): these should not be tracked in the same trace event as they
264 // report stats for the last GC (not the current state). See crbug.com/498779.
Ulan Degenbaev1d316332018-09-03 14:19:08265 std::string object_name_prefix =
266 dump_base_name + "/heap_objects_at_last_gc" + dump_name_suffix;
sside36cfaf2015-06-12 16:42:20267 bool did_dump_object_stats = false;
ssidf51216b02015-06-04 19:46:23268 const size_t object_types =
269 isolate_holder_->isolate()->NumberOfTrackedHeapObjectTypes();
270 for (size_t type_index = 0; type_index < object_types; type_index++) {
271 v8::HeapObjectStatistics object_statistics;
272 if (!isolate_holder_->isolate()->GetHeapObjectStatisticsAtLastGC(
273 &object_statistics, type_index))
274 continue;
275
276 std::string dump_name =
sside36cfaf2015-06-12 16:42:20277 object_name_prefix + "/" + object_statistics.object_type();
ssidf51216b02015-06-04 19:46:23278 if (object_statistics.object_sub_type()[0] != '\0')
279 dump_name += std::string("/") + object_statistics.object_sub_type();
vmpstr1ee03c4c02016-06-30 21:40:56280 auto* object_dump = process_memory_dump->CreateAllocatorDump(dump_name);
ssidf51216b02015-06-04 19:46:23281
282 object_dump->AddScalar(
bratell10f2e3c2015-09-10 16:28:43283 base::trace_event::MemoryAllocatorDump::kNameObjectCount,
ssidf51216b02015-06-04 19:46:23284 base::trace_event::MemoryAllocatorDump::kUnitsObjects,
285 object_statistics.object_count());
sside36cfaf2015-06-12 16:42:20286 object_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
287 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
288 object_statistics.object_size());
289 did_dump_object_stats = true;
ssidf51216b02015-06-04 19:46:23290 }
291
sside36cfaf2015-06-12 16:42:20292 if (process_memory_dump->GetAllocatorDump(object_name_prefix +
293 "/CODE_TYPE")) {
vmpstr1ee03c4c02016-06-30 21:40:56294 auto* code_kind_dump = process_memory_dump->CreateAllocatorDump(
sside36cfaf2015-06-12 16:42:20295 object_name_prefix + "/CODE_TYPE/CODE_KIND");
vmpstr1ee03c4c02016-06-30 21:40:56296 auto* code_age_dump = process_memory_dump->CreateAllocatorDump(
sside36cfaf2015-06-12 16:42:20297 object_name_prefix + "/CODE_TYPE/CODE_AGE");
298 process_memory_dump->AddOwnershipEdge(code_kind_dump->guid(),
299 code_age_dump->guid());
300 }
301
302 if (did_dump_object_stats) {
303 process_memory_dump->AddOwnershipEdge(
304 process_memory_dump->CreateAllocatorDump(object_name_prefix)->guid(),
Ulan Degenbaev1d316332018-09-03 14:19:08305 process_memory_dump->GetOrCreateAllocatorDump(space_name_prefix)
306 ->guid());
sside36cfaf2015-06-12 16:42:20307 }
ssidf51216b02015-06-04 19:46:23308}
309
ssid83aa5be2015-05-08 12:03:26310} // namespace gin