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.
5 #include "gin/v8_isolate_memory_dump_provider.h"
9 #include "base/containers/contains.h"
10 #include "base/task/single_thread_task_runner.h"
11 #include "base/task/thread_pool/thread_pool_instance.h"
12 #include "base/trace_event/process_memory_dump.h"
13 #include "base/trace_event/trace_event.h"
14 #include "build/build_config.h"
15 #include "gin/public/isolate_holder.h"
16 #include "gin/test/v8_test.h"
17 #include "v8/include/v8-initialization.h"
21 class V8MemoryDumpProviderTest : public V8Test {
22 void SetUp() override {
23 // Sets the track objects flag for dumping object statistics. Set this
24 // before initializing V8, because flags should not be modified after
25 // initialization. Also, setting the flag as early as possible ensures more
27 v8::V8::SetFlagsFromString("--track-gc-object-stats");
32 class V8MemoryDumpProviderWorkerTest : public V8MemoryDumpProviderTest {
34 std::unique_ptr<IsolateHolder> CreateIsolateHolder() const override {
35 return std::make_unique<gin::IsolateHolder>(
36 base::SingleThreadTaskRunner::GetCurrentDefault(),
37 gin::IsolateHolder::IsolateType::kBlinkWorkerThread);
41 // Checks if the dump provider runs without crashing and dumps root objects.
42 TEST_F(V8MemoryDumpProviderTest, DumpStatistics) {
43 base::trace_event::MemoryDumpArgs dump_args = {
44 base::trace_event::MemoryDumpLevelOfDetail::kDetailed};
45 std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump(
46 new base::trace_event::ProcessMemoryDump(dump_args));
47 instance_->isolate_memory_dump_provider_for_testing()->OnMemoryDump(
48 dump_args, process_memory_dump.get());
49 const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
50 allocator_dumps = process_memory_dump->allocator_dumps();
52 bool did_dump_isolate_stats = false;
53 bool did_dump_space_stats = false;
54 bool did_dump_objects_stats = false;
55 for (const auto& name_dump : allocator_dumps) {
56 const std::string& name = name_dump.first;
57 if (base::Contains(name, "v8/main")) {
58 did_dump_isolate_stats = true;
60 if (base::Contains(name, "v8/main/heap")) {
61 did_dump_space_stats = true;
63 if (base::Contains(name, "v8/main/heap_objects")) {
64 did_dump_objects_stats = true;
68 ASSERT_TRUE(did_dump_isolate_stats);
69 ASSERT_TRUE(did_dump_space_stats);
70 ASSERT_TRUE(did_dump_objects_stats);
73 TEST_F(V8MemoryDumpProviderTest, DumpGlobalHandlesSize) {
74 base::trace_event::MemoryDumpArgs dump_args = {
75 base::trace_event::MemoryDumpLevelOfDetail::kBackground};
76 std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump(
77 new base::trace_event::ProcessMemoryDump(dump_args));
78 instance_->isolate_memory_dump_provider_for_testing()->OnMemoryDump(
79 dump_args, process_memory_dump.get());
80 const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
81 allocator_dumps = process_memory_dump->allocator_dumps();
83 bool did_dump_global_handles = false;
84 for (const auto& name_dump : allocator_dumps) {
85 const std::string& name = name_dump.first;
86 if (base::Contains(name, "v8/main/global_handles")) {
87 did_dump_global_handles = true;
91 ASSERT_TRUE(did_dump_global_handles);
94 TEST_F(V8MemoryDumpProviderTest, DumpContextStatistics) {
95 base::trace_event::MemoryDumpArgs dump_args = {
96 base::trace_event::MemoryDumpLevelOfDetail::kLight};
97 std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump(
98 new base::trace_event::ProcessMemoryDump(dump_args));
99 instance_->isolate_memory_dump_provider_for_testing()->OnMemoryDump(
100 dump_args, process_memory_dump.get());
101 const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
102 allocator_dumps = process_memory_dump->allocator_dumps();
104 bool did_dump_detached_contexts = false;
105 bool did_dump_native_contexts = false;
106 for (const auto& name_dump : allocator_dumps) {
107 const std::string& name = name_dump.first;
108 if (base::Contains(name, "main/contexts/detached_context")) {
109 did_dump_detached_contexts = true;
111 if (base::Contains(name, "main/contexts/native_context")) {
112 did_dump_native_contexts = true;
116 ASSERT_TRUE(did_dump_detached_contexts);
117 ASSERT_TRUE(did_dump_native_contexts);
120 TEST_F(V8MemoryDumpProviderWorkerTest, DumpContextStatistics) {
121 base::trace_event::MemoryDumpArgs dump_args = {
122 base::trace_event::MemoryDumpLevelOfDetail::kLight};
123 std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump(
124 new base::trace_event::ProcessMemoryDump(dump_args));
125 instance_->isolate_memory_dump_provider_for_testing()->OnMemoryDump(
126 dump_args, process_memory_dump.get());
127 const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
128 allocator_dumps = process_memory_dump->allocator_dumps();
130 bool did_dump_detached_contexts = false;
131 bool did_dump_native_contexts = false;
132 for (const auto& name_dump : allocator_dumps) {
133 const std::string& name = name_dump.first;
134 if (base::Contains(name, "workers/contexts/detached_context/isolate_0x")) {
135 did_dump_detached_contexts = true;
137 if (base::Contains(name, "workers/contexts/native_context/isolate_0x")) {
138 did_dump_native_contexts = true;
142 ASSERT_TRUE(did_dump_detached_contexts);
143 ASSERT_TRUE(did_dump_native_contexts);
146 TEST_F(V8MemoryDumpProviderTest, DumpCodeStatistics) {
147 // Code stats are disabled unless this category is enabled.
148 base::trace_event::TraceLog::GetInstance()->SetEnabled(
149 base::trace_event::TraceConfig(
150 TRACE_DISABLED_BY_DEFAULT("memory-infra.v8.code_stats"), ""),
151 base::trace_event::TraceLog::RECORDING_MODE);
153 base::trace_event::MemoryDumpArgs dump_args = {
154 base::trace_event::MemoryDumpLevelOfDetail::kLight};
155 std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump(
156 new base::trace_event::ProcessMemoryDump(dump_args));
157 instance_->isolate_memory_dump_provider_for_testing()->OnMemoryDump(
158 dump_args, process_memory_dump.get());
159 const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
160 allocator_dumps = process_memory_dump->allocator_dumps();
162 bool did_dump_bytecode_size = false;
163 bool did_dump_code_size = false;
164 bool did_dump_external_scripts_size = false;
165 bool did_dump_cpu_profiler_metadata_size = false;
167 for (const auto& name_dump : allocator_dumps) {
168 const std::string& name = name_dump.first;
169 if (base::Contains(name, "code_stats")) {
170 for (const base::trace_event::MemoryAllocatorDump::Entry& entry :
171 name_dump.second->entries()) {
172 if (base::Contains(entry.name, "bytecode_and_metadata_size")) {
173 did_dump_bytecode_size = true;
174 } else if (base::Contains(entry.name, "code_and_metadata_size")) {
175 did_dump_code_size = true;
176 } else if (base::Contains(entry.name, "external_script_source_size")) {
177 did_dump_external_scripts_size = true;
178 } else if (base::Contains(entry.name, "cpu_profiler_metadata_size")) {
179 did_dump_cpu_profiler_metadata_size = true;
184 base::trace_event::TraceLog::GetInstance()->SetDisabled();
186 ASSERT_TRUE(did_dump_bytecode_size);
187 ASSERT_TRUE(did_dump_code_size);
188 ASSERT_TRUE(did_dump_external_scripts_size);
189 ASSERT_TRUE(did_dump_cpu_profiler_metadata_size);
192 // Tests that a deterministic memory dump request performs a GC.
193 TEST_F(V8MemoryDumpProviderTest, Deterministic) {
194 base::trace_event::MemoryDumpArgs dump_args = {
195 base::trace_event::MemoryDumpLevelOfDetail::kLight,
196 base::trace_event::MemoryDumpDeterminism::kForceGc};
197 std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump(
198 new base::trace_event::ProcessMemoryDump(dump_args));
200 // Allocate an object that has only a weak reference.
201 v8::Global<v8::Object> weak_ref;
203 v8::HandleScope scope(instance_->isolate());
204 v8::Local<v8::Object> object = v8::Object::New(instance_->isolate());
205 weak_ref.Reset(instance_->isolate(), object);
209 // Deterministic memory dump should trigger GC.
210 instance_->isolate_memory_dump_provider_for_testing()->OnMemoryDump(
211 dump_args, process_memory_dump.get());
213 // GC reclaimed the object.
214 ASSERT_TRUE(weak_ref.IsEmpty());