Upload upstream chromium 120.0.6099.5
[platform/framework/web/chromium-efl.git] / gin / v8_isolate_memory_dump_provider.cc
1 // Copyright 2015 The Chromium Authors
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
7 #include <inttypes.h>
8 #include <stddef.h>
9
10 #include "base/check_op.h"
11 #include "base/logging.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/task/single_thread_task_runner.h"
14 #include "base/trace_event/memory_dump_manager.h"
15 #include "base/trace_event/process_memory_dump.h"
16 #include "gin/public/isolate_holder.h"
17 #include "v8/include/v8-isolate.h"
18 #include "v8/include/v8-locker.h"
19 #include "v8/include/v8-statistics.h"
20
21 namespace gin {
22
23 V8IsolateMemoryDumpProvider::V8IsolateMemoryDumpProvider(
24     IsolateHolder* isolate_holder,
25     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
26     : isolate_holder_(isolate_holder) {
27   DCHECK(task_runner);
28   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
29       this, "V8Isolate", task_runner);
30 }
31
32 V8IsolateMemoryDumpProvider::~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.
39 bool V8IsolateMemoryDumpProvider::OnMemoryDump(
40     const base::trace_event::MemoryDumpArgs& args,
41     base::trace_event::ProcessMemoryDump* process_memory_dump) {
42   // TODO(ssid): Use MemoryDumpArgs to create light dumps when requested
43   // (crbug.com/499731).
44
45   if (isolate_holder_->access_mode() == IsolateHolder::kUseLocker) {
46     v8::Locker locked(isolate_holder_->isolate());
47     DumpHeapStatistics(args, process_memory_dump);
48   } else {
49     DumpHeapStatistics(args, process_memory_dump);
50   }
51   return true;
52 }
53
54 namespace {
55
56 // Dump statistics related to code/bytecode when memory-infra.v8.code_stats is
57 // enabled.
58 void DumpCodeStatistics(base::trace_event::MemoryAllocatorDump* dump,
59                         IsolateHolder* isolate_holder) {
60   // 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
78   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());
87   dump->AddScalar("cpu_profiler_metadata_size",
88                   base::trace_event::MemoryAllocatorDump::kUnitsBytes,
89                   code_statistics.cpu_profiler_metadata_size());
90 }
91
92 // Dump the number of native and detached contexts.
93 // The result looks as follows in the Chrome trace viewer:
94 // ========================================
95 // Component                   object_count
96 // - v8
97 //   - main
98 //     - contexts
99 //       - detached_context  10
100 //       - native_context    20
101 //   - workers
102 //     - contexts
103 //       - detached_context
104 //         - isolate_0x1234  10
105 //       - native_context
106 //         - isolate_0x1234  20
107 // ========================================
108 void DumpContextStatistics(
109     base::trace_event::ProcessMemoryDump* process_memory_dump,
110     std::string dump_base_name,
111     std::string dump_name_suffix,
112     size_t number_of_detached_contexts,
113     size_t number_of_native_contexts) {
114   std::string dump_name_prefix = dump_base_name + "/contexts";
115   std::string native_context_name =
116       dump_name_prefix + "/native_context" + dump_name_suffix;
117   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);
122   std::string detached_context_name =
123       dump_name_prefix + "/detached_context" + dump_name_suffix;
124   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
131 std::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
146 bool 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
162 }  // namespace
163
164 void V8IsolateMemoryDumpProvider::DumpHeapStatistics(
165     const base::trace_event::MemoryDumpArgs& args,
166     base::trace_event::ProcessMemoryDump* process_memory_dump) {
167   if (args.determinism == base::trace_event::MemoryDumpDeterminism::kForceGc) {
168     // Force GC in V8 using the same API as DevTools uses in "collectGarbage".
169     isolate_holder_->isolate()->LowMemoryNotification();
170   }
171   std::string isolate_name = base::StringPrintf(
172       "isolate_0x%" PRIXPTR,
173       reinterpret_cast<uintptr_t>(isolate_holder_->isolate()));
174
175   // 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
181   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
188   size_t known_spaces_size = 0;
189   size_t known_spaces_physical_size = 0;
190   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();
197     const size_t space_physical_size = space_statistics.physical_space_size();
198
199     known_spaces_size += space_size;
200     known_spaces_physical_size += space_physical_size;
201
202     std::string space_dump_name = dump_base_name + "/heap/" +
203                                   space_statistics.space_name() +
204                                   dump_name_suffix;
205
206     auto* space_dump =
207         process_memory_dump->CreateAllocatorDump(space_dump_name);
208     space_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
209                           base::trace_event::MemoryAllocatorDump::kUnitsBytes,
210                           space_physical_size);
211     space_dump->AddScalar("virtual_size",
212                           base::trace_event::MemoryAllocatorDump::kUnitsBytes,
213                           space_size);
214
215     space_dump->AddScalar("allocated_objects_size",
216                           base::trace_event::MemoryAllocatorDump::kUnitsBytes,
217                           space_used_size);
218   }
219
220   // Sanity check that all spaces are accounted for in GetHeapSpaceStatistics.
221   // Background threads may be running and allocating concurrently, so the sum
222   // of space sizes may exceed the total heap size that was sampled earlier.
223   DCHECK_LE(heap_statistics.total_heap_size(), known_spaces_size);
224
225   // 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()) {
229     auto* zap_dump = process_memory_dump->CreateAllocatorDump(
230         dump_base_name + "/zapped_for_debug" + dump_name_suffix);
231     size_t zapped_size_for_debugging =
232         known_spaces_size >= known_spaces_physical_size
233             ? known_spaces_size - known_spaces_physical_size
234             : 0;
235     zap_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
236                         base::trace_event::MemoryAllocatorDump::kUnitsBytes,
237                         zapped_size_for_debugging);
238   }
239
240   // Dump statistics about malloced memory.
241   std::string malloc_name = dump_base_name + "/malloc" + dump_name_suffix;
242   auto* malloc_dump = process_memory_dump->CreateAllocatorDump(malloc_name);
243   malloc_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
244                          base::trace_event::MemoryAllocatorDump::kUnitsBytes,
245                          heap_statistics.malloced_memory());
246   malloc_dump->AddScalar("peak_size",
247                          base::trace_event::MemoryAllocatorDump::kUnitsBytes,
248                          heap_statistics.peak_malloced_memory());
249   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
257   DumpContextStatistics(process_memory_dump, dump_base_name, dump_name_suffix,
258                         heap_statistics.number_of_detached_contexts(),
259                         heap_statistics.number_of_native_contexts());
260
261   auto* code_stats_dump = process_memory_dump->CreateAllocatorDump(
262       dump_base_name + "/code_stats" + dump_name_suffix);
263
264   // Dump statistics related to code and bytecode if requested.
265   DumpCodeStatistics(code_stats_dump, isolate_holder_);
266
267   // 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());
274   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   }
282
283   // Dump object statistics only for detailed dumps.
284   if (args.level_of_detail !=
285       base::trace_event::MemoryDumpLevelOfDetail::kDetailed) {
286     return;
287   }
288
289   // Dump statistics of the heap's live objects from last GC.
290   // 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.
292   std::string object_name_prefix =
293       dump_base_name + "/heap_objects_at_last_gc" + dump_name_suffix;
294   bool did_dump_object_stats = false;
295   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 =
304         object_name_prefix + "/" + object_statistics.object_type();
305     if (object_statistics.object_sub_type()[0] != '\0')
306       dump_name += std::string("/") + object_statistics.object_sub_type();
307     auto* object_dump = process_memory_dump->CreateAllocatorDump(dump_name);
308
309     object_dump->AddScalar(
310         base::trace_event::MemoryAllocatorDump::kNameObjectCount,
311         base::trace_event::MemoryAllocatorDump::kUnitsObjects,
312         object_statistics.object_count());
313     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;
317   }
318
319   if (process_memory_dump->GetAllocatorDump(object_name_prefix +
320                                             "/CODE_TYPE")) {
321     auto* code_kind_dump = process_memory_dump->CreateAllocatorDump(
322         object_name_prefix + "/CODE_TYPE/CODE_KIND");
323     auto* code_age_dump = process_memory_dump->CreateAllocatorDump(
324         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(),
332         process_memory_dump->GetOrCreateAllocatorDump(space_name_prefix)
333             ->guid());
334   }
335 }
336
337 }  // namespace gin