Line data Source code
1 : // Copyright 2011 the V8 project authors. All rights reserved.
2 : // Redistribution and use in source and binary forms, with or without
3 : // modification, are permitted provided that the following conditions are
4 : // met:
5 : //
6 : // * Redistributions of source code must retain the above copyright
7 : // notice, this list of conditions and the following disclaimer.
8 : // * Redistributions in binary form must reproduce the above
9 : // copyright notice, this list of conditions and the following
10 : // disclaimer in the documentation and/or other materials provided
11 : // with the distribution.
12 : // * Neither the name of Google Inc. nor the names of its
13 : // contributors may be used to endorse or promote products derived
14 : // from this software without specific prior written permission.
15 : //
16 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : //
28 : // Tests for heap profiler
29 :
30 : #include <ctype.h>
31 :
32 : #include <memory>
33 :
34 : #include "src/v8.h"
35 :
36 : #include "include/v8-profiler.h"
37 : #include "src/api-inl.h"
38 : #include "src/assembler-inl.h"
39 : #include "src/base/hashmap.h"
40 : #include "src/base/optional.h"
41 : #include "src/collector.h"
42 : #include "src/debug/debug.h"
43 : #include "src/heap/heap-inl.h"
44 : #include "src/objects-inl.h"
45 : #include "src/profiler/allocation-tracker.h"
46 : #include "src/profiler/heap-profiler.h"
47 : #include "src/profiler/heap-snapshot-generator-inl.h"
48 : #include "test/cctest/cctest.h"
49 :
50 : using i::AllocationTraceNode;
51 : using i::AllocationTraceTree;
52 : using i::AllocationTracker;
53 : using i::ArrayVector;
54 : using i::SourceLocation;
55 : using i::Vector;
56 : using v8::base::Optional;
57 :
58 : namespace {
59 :
60 : class NamedEntriesDetector {
61 : public:
62 : NamedEntriesDetector()
63 5 : : has_A2(false), has_B2(false), has_C2(false) {
64 : }
65 :
66 21365 : void CheckEntry(i::HeapEntry* entry) {
67 21365 : if (strcmp(entry->name(), "A2") == 0) has_A2 = true;
68 21365 : if (strcmp(entry->name(), "B2") == 0) has_B2 = true;
69 21365 : if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
70 21365 : }
71 :
72 5 : void CheckAllReachables(i::HeapEntry* root) {
73 : v8::base::HashMap visited;
74 : std::vector<i::HeapEntry*> list;
75 5 : list.push_back(root);
76 5 : CheckEntry(root);
77 21370 : while (!list.empty()) {
78 21365 : i::HeapEntry* entry = list.back();
79 : list.pop_back();
80 168215 : for (int i = 0; i < entry->children_count(); ++i) {
81 : i::HeapGraphEdge* edge = entry->child(i);
82 125490 : if (edge->type() == i::HeapGraphEdge::kShortcut) continue;
83 73425 : i::HeapEntry* child = edge->to();
84 146850 : v8::base::HashMap::Entry* entry = visited.LookupOrInsert(
85 : reinterpret_cast<void*>(child),
86 : static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)));
87 73425 : if (entry->value)
88 : continue;
89 21360 : entry->value = reinterpret_cast<void*>(1);
90 21360 : list.push_back(child);
91 21360 : CheckEntry(child);
92 : }
93 : }
94 5 : }
95 :
96 : bool has_A2;
97 : bool has_B2;
98 : bool has_C2;
99 : };
100 :
101 : } // namespace
102 :
103 :
104 249 : static const v8::HeapGraphNode* GetGlobalObject(
105 : const v8::HeapSnapshot* snapshot) {
106 : // The 0th-child is (GC Roots), 1st is the user root.
107 : const v8::HeapGraphNode* global_obj =
108 249 : snapshot->GetRoot()->GetChild(1)->GetToNode();
109 249 : CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
110 : reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
111 249 : return global_obj;
112 : }
113 :
114 : static const char* GetName(const v8::HeapGraphNode* node) {
115 : return const_cast<i::HeapEntry*>(reinterpret_cast<const i::HeapEntry*>(node))
116 : ->name();
117 : }
118 :
119 : static const char* GetName(const v8::HeapGraphEdge* edge) {
120 : return const_cast<i::HeapGraphEdge*>(
121 : reinterpret_cast<const i::HeapGraphEdge*>(edge))
122 : ->name();
123 : }
124 :
125 : static size_t GetSize(const v8::HeapGraphNode* node) {
126 : return const_cast<i::HeapEntry*>(reinterpret_cast<const i::HeapEntry*>(node))
127 : ->self_size();
128 : }
129 :
130 105 : static const v8::HeapGraphNode* GetChildByName(const v8::HeapGraphNode* node,
131 : const char* name) {
132 2777 : for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
133 2762 : const v8::HeapGraphNode* child = node->GetChild(i)->GetToNode();
134 2762 : if (!strcmp(name, GetName(child))) {
135 : return child;
136 : }
137 : }
138 : return nullptr;
139 : }
140 :
141 15 : static const v8::HeapGraphEdge* GetEdgeByChildName(
142 : const v8::HeapGraphNode* node, const char* name) {
143 334 : for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
144 334 : const v8::HeapGraphEdge* edge = node->GetChild(i);
145 334 : const v8::HeapGraphNode* child = edge->GetToNode();
146 334 : if (!strcmp(name, GetName(child))) {
147 : return edge;
148 : }
149 : }
150 : return nullptr;
151 : }
152 :
153 : static const v8::HeapGraphNode* GetRootChild(const v8::HeapSnapshot* snapshot,
154 : const char* name) {
155 10 : return GetChildByName(snapshot->GetRoot(), name);
156 : }
157 :
158 : static Optional<SourceLocation> GetLocation(const v8::HeapSnapshot* s,
159 : const v8::HeapGraphNode* node) {
160 : const i::HeapSnapshot* snapshot = reinterpret_cast<const i::HeapSnapshot*>(s);
161 : const std::vector<SourceLocation>& locations = snapshot->locations();
162 : const i::HeapEntry* entry = reinterpret_cast<const i::HeapEntry*>(node);
163 240 : for (const auto& loc : locations) {
164 480 : if (loc.entry_index == entry->index()) {
165 : return Optional<SourceLocation>(loc);
166 : }
167 : }
168 :
169 : return Optional<SourceLocation>();
170 : }
171 :
172 3152 : static const v8::HeapGraphNode* GetProperty(v8::Isolate* isolate,
173 : const v8::HeapGraphNode* node,
174 : v8::HeapGraphEdge::Type type,
175 : const char* name) {
176 6007019 : for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
177 6006558 : const v8::HeapGraphEdge* prop = node->GetChild(i);
178 12010425 : v8::String::Utf8Value prop_name(isolate, prop->GetName());
179 6006558 : if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
180 2691 : return prop->GetToNode();
181 : }
182 : return nullptr;
183 : }
184 :
185 5 : static bool HasString(v8::Isolate* isolate, const v8::HeapGraphNode* node,
186 : const char* contents) {
187 5 : for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
188 5 : const v8::HeapGraphEdge* prop = node->GetChild(i);
189 5 : const v8::HeapGraphNode* node = prop->GetToNode();
190 5 : if (node->GetType() == v8::HeapGraphNode::kString) {
191 10 : v8::String::Utf8Value node_name(isolate, node->GetName());
192 5 : if (strcmp(contents, *node_name) == 0) return true;
193 : }
194 : }
195 : return false;
196 : }
197 :
198 20 : static void EnsureNoUninstrumentedInternals(v8::Isolate* isolate,
199 : const v8::HeapGraphNode* node) {
200 820 : for (int i = 0; i < 20; ++i) {
201 : i::ScopedVector<char> buffer(10);
202 : const v8::HeapGraphNode* internal =
203 400 : GetProperty(isolate, node, v8::HeapGraphEdge::kInternal,
204 400 : i::IntToCString(i, buffer));
205 400 : CHECK(!internal);
206 : }
207 20 : }
208 :
209 : // Check that snapshot has no unretained entries except root.
210 318 : static bool ValidateSnapshot(const v8::HeapSnapshot* snapshot, int depth = 3) {
211 : i::HeapSnapshot* heap_snapshot = const_cast<i::HeapSnapshot*>(
212 : reinterpret_cast<const i::HeapSnapshot*>(snapshot));
213 :
214 : v8::base::HashMap visited;
215 : std::deque<i::HeapGraphEdge>& edges = heap_snapshot->edges();
216 18253394 : for (size_t i = 0; i < edges.size(); ++i) {
217 27379614 : v8::base::HashMap::Entry* entry = visited.LookupOrInsert(
218 : reinterpret_cast<void*>(edges[i].to()),
219 : static_cast<uint32_t>(reinterpret_cast<uintptr_t>(edges[i].to())));
220 : uint32_t ref_count = static_cast<uint32_t>(
221 9126538 : reinterpret_cast<uintptr_t>(entry->value));
222 9126538 : entry->value = reinterpret_cast<void*>(ref_count + 1);
223 : }
224 : uint32_t unretained_entries_count = 0;
225 : std::deque<i::HeapEntry>& entries = heap_snapshot->entries();
226 2584715 : for (i::HeapEntry& entry : entries) {
227 2584397 : v8::base::HashMap::Entry* map_entry = visited.Lookup(
228 : reinterpret_cast<void*>(&entry),
229 : static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&entry)));
230 2584397 : if (!map_entry && entry.id() != 1) {
231 0 : entry.Print("entry with no retainer", "", depth, 0);
232 0 : ++unretained_entries_count;
233 : }
234 : }
235 636 : return unretained_entries_count == 0;
236 : }
237 :
238 20 : bool EndsWith(const char* a, const char* b) {
239 20 : size_t length_a = strlen(a);
240 20 : size_t length_b = strlen(b);
241 20 : return (length_a >= length_b) && !strcmp(a + length_a - length_b, b);
242 : }
243 :
244 26644 : TEST(HeapSnapshot) {
245 5 : LocalContext env2;
246 10 : v8::HandleScope scope(env2->GetIsolate());
247 5 : v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler();
248 :
249 : CompileRun(
250 : "function A2() {}\n"
251 : "function B2(x) { return function() { return typeof x; }; }\n"
252 : "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
253 : "var a2 = new A2();\n"
254 : "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
255 : "var c2 = new C2(a2);");
256 5 : const v8::HeapSnapshot* snapshot_env2 = heap_profiler->TakeHeapSnapshot();
257 5 : CHECK(ValidateSnapshot(snapshot_env2));
258 5 : const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
259 :
260 : // Verify, that JS global object of env2 has '..2' properties.
261 5 : const v8::HeapGraphNode* a2_node = GetProperty(
262 5 : env2->GetIsolate(), global_env2, v8::HeapGraphEdge::kProperty, "a2");
263 5 : CHECK(a2_node);
264 5 : CHECK(GetProperty(env2->GetIsolate(), global_env2,
265 : v8::HeapGraphEdge::kProperty, "b2_1"));
266 5 : CHECK(GetProperty(env2->GetIsolate(), global_env2,
267 : v8::HeapGraphEdge::kProperty, "b2_2"));
268 5 : CHECK(GetProperty(env2->GetIsolate(), global_env2,
269 : v8::HeapGraphEdge::kProperty, "c2"));
270 :
271 : NamedEntriesDetector det;
272 : det.CheckAllReachables(const_cast<i::HeapEntry*>(
273 5 : reinterpret_cast<const i::HeapEntry*>(global_env2)));
274 5 : CHECK(det.has_A2);
275 5 : CHECK(det.has_B2);
276 5 : CHECK(det.has_C2);
277 5 : }
278 :
279 26644 : TEST(HeapSnapshotLocations) {
280 5 : LocalContext env;
281 10 : v8::HandleScope scope(env->GetIsolate());
282 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
283 :
284 : CompileRun(
285 : "function X(a) { return function() { return a; } }\n"
286 : "function* getid() { yield 1; }\n"
287 : "class A {}\n"
288 : "var x = X(1);\n"
289 : "var g = getid();\n"
290 : "var o = new A();");
291 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
292 5 : CHECK(ValidateSnapshot(snapshot));
293 :
294 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
295 : const v8::HeapGraphNode* x =
296 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "x");
297 5 : CHECK(x);
298 :
299 : Optional<SourceLocation> x_loc = GetLocation(snapshot, x);
300 5 : CHECK(x_loc);
301 5 : CHECK_EQ(0, x_loc->line);
302 5 : CHECK_EQ(31, x_loc->col);
303 :
304 : const v8::HeapGraphNode* g =
305 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "g");
306 5 : CHECK(x);
307 :
308 : Optional<SourceLocation> g_loc = GetLocation(snapshot, g);
309 5 : CHECK(g_loc);
310 5 : CHECK_EQ(1, g_loc->line);
311 5 : CHECK_EQ(15, g_loc->col);
312 :
313 : const v8::HeapGraphNode* o =
314 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "o");
315 5 : CHECK(x);
316 :
317 : Optional<SourceLocation> o_loc = GetLocation(snapshot, o);
318 5 : CHECK(o_loc);
319 5 : CHECK_EQ(2, o_loc->line);
320 5 : CHECK_EQ(0, o_loc->col);
321 5 : }
322 :
323 26644 : TEST(HeapSnapshotObjectSizes) {
324 5 : LocalContext env;
325 10 : v8::HandleScope scope(env->GetIsolate());
326 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
327 :
328 : // -a-> X1 --a
329 : // x -b-> X2 <-|
330 : CompileRun(
331 : "function X(a, b) { this.a = a; this.b = b; }\n"
332 : "x = new X(new X(), new X());\n"
333 : "dummy = new X();\n"
334 : "(function() { x.a.a = x.b; })();");
335 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
336 5 : CHECK(ValidateSnapshot(snapshot));
337 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
338 : const v8::HeapGraphNode* x =
339 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "x");
340 5 : CHECK(x);
341 : const v8::HeapGraphNode* x1 =
342 5 : GetProperty(env->GetIsolate(), x, v8::HeapGraphEdge::kProperty, "a");
343 5 : CHECK(x1);
344 : const v8::HeapGraphNode* x2 =
345 5 : GetProperty(env->GetIsolate(), x, v8::HeapGraphEdge::kProperty, "b");
346 5 : CHECK(x2);
347 :
348 : // Test sizes.
349 5 : CHECK_NE(0, static_cast<int>(x->GetShallowSize()));
350 5 : CHECK_NE(0, static_cast<int>(x1->GetShallowSize()));
351 5 : CHECK_NE(0, static_cast<int>(x2->GetShallowSize()));
352 5 : }
353 :
354 :
355 26644 : TEST(BoundFunctionInSnapshot) {
356 5 : LocalContext env;
357 10 : v8::HandleScope scope(env->GetIsolate());
358 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
359 : CompileRun(
360 : "function myFunction(a, b) { this.a = a; this.b = b; }\n"
361 : "function AAAAA() {}\n"
362 : "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
363 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
364 5 : CHECK(ValidateSnapshot(snapshot));
365 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
366 5 : const v8::HeapGraphNode* f = GetProperty(
367 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "boundFunction");
368 5 : CHECK(f);
369 20 : CHECK(v8_str("native_bind")->Equals(env.local(), f->GetName()).FromJust());
370 5 : const v8::HeapGraphNode* bindings = GetProperty(
371 5 : env->GetIsolate(), f, v8::HeapGraphEdge::kInternal, "bindings");
372 5 : CHECK(bindings);
373 5 : CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
374 5 : CHECK_EQ(1, bindings->GetChildrenCount());
375 :
376 5 : const v8::HeapGraphNode* bound_this = GetProperty(
377 5 : env->GetIsolate(), f, v8::HeapGraphEdge::kInternal, "bound_this");
378 5 : CHECK(bound_this);
379 5 : CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());
380 :
381 5 : const v8::HeapGraphNode* bound_function = GetProperty(
382 5 : env->GetIsolate(), f, v8::HeapGraphEdge::kInternal, "bound_function");
383 5 : CHECK(bound_function);
384 5 : CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());
385 :
386 5 : const v8::HeapGraphNode* bound_argument = GetProperty(
387 5 : env->GetIsolate(), f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
388 5 : CHECK(bound_argument);
389 5 : CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
390 5 : }
391 :
392 :
393 26644 : TEST(HeapSnapshotEntryChildren) {
394 5 : LocalContext env;
395 10 : v8::HandleScope scope(env->GetIsolate());
396 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
397 :
398 : CompileRun(
399 : "function A() { }\n"
400 : "a = new A;");
401 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
402 5 : CHECK(ValidateSnapshot(snapshot));
403 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
404 334 : for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
405 329 : const v8::HeapGraphEdge* prop = global->GetChild(i);
406 329 : CHECK_EQ(global, prop->GetFromNode());
407 : }
408 : const v8::HeapGraphNode* a =
409 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
410 5 : CHECK(a);
411 15 : for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
412 10 : const v8::HeapGraphEdge* prop = a->GetChild(i);
413 10 : CHECK_EQ(a, prop->GetFromNode());
414 : }
415 5 : }
416 :
417 :
418 26644 : TEST(HeapSnapshotCodeObjects) {
419 5 : LocalContext env;
420 10 : v8::HandleScope scope(env->GetIsolate());
421 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
422 :
423 : CompileRun(
424 : "function lazy(x) { return x - 1; }\n"
425 : "function compiled(x) { ()=>x; return x + 1; }\n"
426 : "var anonymous = (function() { return function() { return 0; } })();\n"
427 : "compiled(1)");
428 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
429 5 : CHECK(ValidateSnapshot(snapshot));
430 :
431 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
432 5 : const v8::HeapGraphNode* compiled = GetProperty(
433 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "compiled");
434 5 : CHECK(compiled);
435 5 : CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
436 5 : const v8::HeapGraphNode* lazy = GetProperty(
437 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "lazy");
438 5 : CHECK(lazy);
439 5 : CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
440 5 : const v8::HeapGraphNode* anonymous = GetProperty(
441 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "anonymous");
442 5 : CHECK(anonymous);
443 5 : CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
444 15 : v8::String::Utf8Value anonymous_name(env->GetIsolate(), anonymous->GetName());
445 5 : CHECK_EQ(0, strcmp("", *anonymous_name));
446 :
447 : // Find references to shared function info.
448 5 : const v8::HeapGraphNode* compiled_sfi = GetProperty(
449 5 : env->GetIsolate(), compiled, v8::HeapGraphEdge::kInternal, "shared");
450 5 : CHECK(compiled_sfi);
451 5 : const v8::HeapGraphNode* lazy_sfi = GetProperty(
452 5 : env->GetIsolate(), lazy, v8::HeapGraphEdge::kInternal, "shared");
453 5 : CHECK(lazy_sfi);
454 :
455 : // TODO(leszeks): Check that there's bytecode on the compiled function, but
456 : // not the lazy function.
457 :
458 : // Verify that non-compiled function doesn't contain references to "x"
459 : // literal, while compiled function does. The scope info is stored in
460 : // FixedArray objects attached to the SharedFunctionInfo.
461 : bool compiled_references_x = false, lazy_references_x = false;
462 5 : for (int i = 0, count = compiled_sfi->GetChildrenCount(); i < count; ++i) {
463 5 : const v8::HeapGraphEdge* prop = compiled_sfi->GetChild(i);
464 5 : const v8::HeapGraphNode* node = prop->GetToNode();
465 5 : if (node->GetType() == v8::HeapGraphNode::kArray) {
466 5 : if (HasString(env->GetIsolate(), node, "x")) {
467 : compiled_references_x = true;
468 : break;
469 : }
470 : }
471 : }
472 20 : for (int i = 0, count = lazy_sfi->GetChildrenCount(); i < count; ++i) {
473 15 : const v8::HeapGraphEdge* prop = lazy_sfi->GetChild(i);
474 15 : const v8::HeapGraphNode* node = prop->GetToNode();
475 15 : if (node->GetType() == v8::HeapGraphNode::kArray) {
476 0 : if (HasString(env->GetIsolate(), node, "x")) {
477 : lazy_references_x = true;
478 : break;
479 : }
480 : }
481 : }
482 5 : CHECK(compiled_references_x);
483 5 : if (i::FLAG_lazy) {
484 5 : CHECK(!lazy_references_x);
485 : }
486 5 : }
487 :
488 :
489 26644 : TEST(HeapSnapshotHeapNumbers) {
490 5 : LocalContext env;
491 10 : v8::HandleScope scope(env->GetIsolate());
492 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
493 : CompileRun(
494 : "a = 1; // a is Smi\n"
495 : "b = 2.5; // b is HeapNumber");
496 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
497 5 : CHECK(ValidateSnapshot(snapshot));
498 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
499 5 : CHECK(!GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty,
500 : "a"));
501 : const v8::HeapGraphNode* b =
502 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "b");
503 5 : CHECK(b);
504 5 : CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
505 5 : }
506 :
507 26644 : TEST(HeapSnapshotHeapBigInts) {
508 5 : LocalContext env;
509 10 : v8::HandleScope scope(env->GetIsolate());
510 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
511 : CompileRun(
512 : "a = 1n;"
513 : "b = Object(BigInt(2))");
514 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
515 5 : CHECK(ValidateSnapshot(snapshot));
516 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
517 : const v8::HeapGraphNode* a =
518 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
519 5 : CHECK(a);
520 5 : CHECK_EQ(v8::HeapGraphNode::kBigInt, a->GetType());
521 : const v8::HeapGraphNode* b =
522 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "b");
523 5 : CHECK(b);
524 5 : CHECK_EQ(v8::HeapGraphNode::kObject, b->GetType());
525 5 : }
526 :
527 26644 : TEST(HeapSnapshotSlicedString) {
528 : if (!i::FLAG_string_slices) return;
529 5 : LocalContext env;
530 10 : v8::HandleScope scope(env->GetIsolate());
531 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
532 : CompileRun(
533 : "parent_string = \"123456789.123456789.123456789.123456789.123456789."
534 : "123456789.123456789.123456789.123456789.123456789."
535 : "123456789.123456789.123456789.123456789.123456789."
536 : "123456789.123456789.123456789.123456789.123456789."
537 : "123456789.123456789.123456789.123456789.123456789."
538 : "123456789.123456789.123456789.123456789.123456789."
539 : "123456789.123456789.123456789.123456789.123456789."
540 : "123456789.123456789.123456789.123456789.123456789.\";"
541 : "child_string = parent_string.slice(100);");
542 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
543 5 : CHECK(ValidateSnapshot(snapshot));
544 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
545 5 : const v8::HeapGraphNode* parent_string = GetProperty(
546 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "parent_string");
547 5 : CHECK(parent_string);
548 5 : const v8::HeapGraphNode* child_string = GetProperty(
549 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "child_string");
550 5 : CHECK(child_string);
551 5 : CHECK_EQ(v8::HeapGraphNode::kSlicedString, child_string->GetType());
552 5 : const v8::HeapGraphNode* parent = GetProperty(
553 5 : env->GetIsolate(), child_string, v8::HeapGraphEdge::kInternal, "parent");
554 5 : CHECK_EQ(parent_string, parent);
555 5 : heap_profiler->DeleteAllHeapSnapshots();
556 : }
557 :
558 :
559 26644 : TEST(HeapSnapshotConsString) {
560 5 : v8::Isolate* isolate = CcTest::isolate();
561 10 : v8::HandleScope scope(isolate);
562 : v8::Local<v8::ObjectTemplate> global_template =
563 5 : v8::ObjectTemplate::New(isolate);
564 5 : global_template->SetInternalFieldCount(1);
565 5 : LocalContext env(nullptr, global_template);
566 5 : v8::Local<v8::Object> global_proxy = env->Global();
567 5 : v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
568 5 : CHECK_EQ(1, global->InternalFieldCount());
569 :
570 : i::Factory* factory = CcTest::i_isolate()->factory();
571 5 : i::Handle<i::String> first = factory->NewStringFromStaticChars("0123456789");
572 5 : i::Handle<i::String> second = factory->NewStringFromStaticChars("0123456789");
573 : i::Handle<i::String> cons_string =
574 10 : factory->NewConsString(first, second).ToHandleChecked();
575 :
576 5 : global->SetInternalField(0, v8::ToApiHandle<v8::String>(cons_string));
577 :
578 5 : v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
579 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
580 5 : CHECK(ValidateSnapshot(snapshot));
581 5 : const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
582 :
583 : const v8::HeapGraphNode* string_node =
584 5 : GetProperty(isolate, global_node, v8::HeapGraphEdge::kInternal, "0");
585 5 : CHECK(string_node);
586 5 : CHECK_EQ(v8::HeapGraphNode::kConsString, string_node->GetType());
587 :
588 : const v8::HeapGraphNode* first_node =
589 5 : GetProperty(isolate, string_node, v8::HeapGraphEdge::kInternal, "first");
590 5 : CHECK_EQ(v8::HeapGraphNode::kString, first_node->GetType());
591 :
592 : const v8::HeapGraphNode* second_node =
593 5 : GetProperty(isolate, string_node, v8::HeapGraphEdge::kInternal, "second");
594 5 : CHECK_EQ(v8::HeapGraphNode::kString, second_node->GetType());
595 :
596 5 : heap_profiler->DeleteAllHeapSnapshots();
597 5 : }
598 :
599 :
600 26644 : TEST(HeapSnapshotSymbol) {
601 5 : LocalContext env;
602 10 : v8::HandleScope scope(env->GetIsolate());
603 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
604 :
605 : CompileRun("a = Symbol('mySymbol');\n");
606 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
607 5 : CHECK(ValidateSnapshot(snapshot));
608 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
609 : const v8::HeapGraphNode* a =
610 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
611 5 : CHECK(a);
612 5 : CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol);
613 20 : CHECK(v8_str("symbol")->Equals(env.local(), a->GetName()).FromJust());
614 : const v8::HeapGraphNode* name =
615 5 : GetProperty(env->GetIsolate(), a, v8::HeapGraphEdge::kInternal, "name");
616 5 : CHECK(name);
617 20 : CHECK(v8_str("mySymbol")->Equals(env.local(), name->GetName()).FromJust());
618 5 : }
619 :
620 26644 : TEST(HeapSnapshotWeakCollection) {
621 5 : LocalContext env;
622 10 : v8::HandleScope scope(env->GetIsolate());
623 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
624 :
625 : CompileRun(
626 : "k = {}; v = {}; s = 'str';\n"
627 : "ws = new WeakSet(); ws.add(k); ws.add(v); ws[s] = s;\n"
628 : "wm = new WeakMap(); wm.set(k, v); wm[s] = s;\n");
629 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
630 5 : CHECK(ValidateSnapshot(snapshot));
631 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
632 : const v8::HeapGraphNode* k =
633 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "k");
634 5 : CHECK(k);
635 : const v8::HeapGraphNode* v =
636 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "v");
637 5 : CHECK(v);
638 : const v8::HeapGraphNode* s =
639 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "s");
640 5 : CHECK(s);
641 :
642 5 : const v8::HeapGraphNode* ws = GetProperty(env->GetIsolate(), global,
643 5 : v8::HeapGraphEdge::kProperty, "ws");
644 5 : CHECK(ws);
645 5 : CHECK_EQ(v8::HeapGraphNode::kObject, ws->GetType());
646 20 : CHECK(v8_str("WeakSet")->Equals(env.local(), ws->GetName()).FromJust());
647 :
648 : const v8::HeapGraphNode* ws_table =
649 5 : GetProperty(env->GetIsolate(), ws, v8::HeapGraphEdge::kInternal, "table");
650 5 : CHECK_EQ(v8::HeapGraphNode::kArray, ws_table->GetType());
651 5 : CHECK_GT(ws_table->GetChildrenCount(), 0);
652 : int weak_entries = 0;
653 20 : for (int i = 0, count = ws_table->GetChildrenCount(); i < count; ++i) {
654 15 : const v8::HeapGraphEdge* prop = ws_table->GetChild(i);
655 15 : if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
656 10 : if (k->GetId() == prop->GetToNode()->GetId()) {
657 5 : ++weak_entries;
658 : }
659 : }
660 5 : CHECK_EQ(1, weak_entries);
661 : const v8::HeapGraphNode* ws_s =
662 5 : GetProperty(env->GetIsolate(), ws, v8::HeapGraphEdge::kProperty, "str");
663 5 : CHECK(ws_s);
664 5 : CHECK_EQ(s->GetId(), ws_s->GetId());
665 :
666 5 : const v8::HeapGraphNode* wm = GetProperty(env->GetIsolate(), global,
667 5 : v8::HeapGraphEdge::kProperty, "wm");
668 5 : CHECK(wm);
669 5 : CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType());
670 20 : CHECK(v8_str("WeakMap")->Equals(env.local(), wm->GetName()).FromJust());
671 :
672 : const v8::HeapGraphNode* wm_table =
673 5 : GetProperty(env->GetIsolate(), wm, v8::HeapGraphEdge::kInternal, "table");
674 5 : CHECK_EQ(v8::HeapGraphNode::kArray, wm_table->GetType());
675 5 : CHECK_GT(wm_table->GetChildrenCount(), 0);
676 : weak_entries = 0;
677 20 : for (int i = 0, count = wm_table->GetChildrenCount(); i < count; ++i) {
678 15 : const v8::HeapGraphEdge* prop = wm_table->GetChild(i);
679 15 : if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
680 10 : const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
681 10 : if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
682 10 : ++weak_entries;
683 : }
684 : }
685 5 : CHECK_EQ(2, weak_entries); // Key and value are weak.
686 : const v8::HeapGraphNode* wm_s =
687 5 : GetProperty(env->GetIsolate(), wm, v8::HeapGraphEdge::kProperty, "str");
688 5 : CHECK(wm_s);
689 5 : CHECK_EQ(s->GetId(), wm_s->GetId());
690 5 : }
691 :
692 :
693 26644 : TEST(HeapSnapshotCollection) {
694 5 : LocalContext env;
695 10 : v8::HandleScope scope(env->GetIsolate());
696 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
697 :
698 : CompileRun(
699 : "k = {}; v = {}; s = 'str';\n"
700 : "set = new Set(); set.add(k); set.add(v); set[s] = s;\n"
701 : "map = new Map(); map.set(k, v); map[s] = s;\n");
702 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
703 5 : CHECK(ValidateSnapshot(snapshot));
704 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
705 : const v8::HeapGraphNode* k =
706 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "k");
707 5 : CHECK(k);
708 : const v8::HeapGraphNode* v =
709 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "v");
710 5 : CHECK(v);
711 : const v8::HeapGraphNode* s =
712 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "s");
713 5 : CHECK(s);
714 :
715 5 : const v8::HeapGraphNode* set = GetProperty(
716 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "set");
717 5 : CHECK(set);
718 5 : CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType());
719 20 : CHECK(v8_str("Set")->Equals(env.local(), set->GetName()).FromJust());
720 :
721 5 : const v8::HeapGraphNode* set_table = GetProperty(
722 5 : env->GetIsolate(), set, v8::HeapGraphEdge::kInternal, "table");
723 5 : CHECK_EQ(v8::HeapGraphNode::kArray, set_table->GetType());
724 5 : CHECK_GT(set_table->GetChildrenCount(), 0);
725 : int entries = 0;
726 20 : for (int i = 0, count = set_table->GetChildrenCount(); i < count; ++i) {
727 15 : const v8::HeapGraphEdge* prop = set_table->GetChild(i);
728 15 : const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
729 15 : if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
730 10 : ++entries;
731 : }
732 : }
733 5 : CHECK_EQ(2, entries);
734 : const v8::HeapGraphNode* set_s =
735 5 : GetProperty(env->GetIsolate(), set, v8::HeapGraphEdge::kProperty, "str");
736 5 : CHECK(set_s);
737 5 : CHECK_EQ(s->GetId(), set_s->GetId());
738 :
739 5 : const v8::HeapGraphNode* map = GetProperty(
740 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "map");
741 5 : CHECK(map);
742 5 : CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType());
743 20 : CHECK(v8_str("Map")->Equals(env.local(), map->GetName()).FromJust());
744 :
745 5 : const v8::HeapGraphNode* map_table = GetProperty(
746 5 : env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "table");
747 5 : CHECK_EQ(v8::HeapGraphNode::kArray, map_table->GetType());
748 5 : CHECK_GT(map_table->GetChildrenCount(), 0);
749 : entries = 0;
750 20 : for (int i = 0, count = map_table->GetChildrenCount(); i < count; ++i) {
751 15 : const v8::HeapGraphEdge* prop = map_table->GetChild(i);
752 15 : const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
753 15 : if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
754 10 : ++entries;
755 : }
756 : }
757 5 : CHECK_EQ(2, entries);
758 : const v8::HeapGraphNode* map_s =
759 5 : GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kProperty, "str");
760 5 : CHECK(map_s);
761 5 : CHECK_EQ(s->GetId(), map_s->GetId());
762 5 : }
763 :
764 26644 : TEST(HeapSnapshotMap) {
765 5 : LocalContext env;
766 10 : v8::HandleScope scope(env->GetIsolate());
767 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
768 :
769 : CompileRun(
770 : "function Z() { this.foo = {}; this.bar = 0; }\n"
771 : "z = new Z();\n");
772 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
773 5 : CHECK(ValidateSnapshot(snapshot));
774 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
775 : const v8::HeapGraphNode* z =
776 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "z");
777 5 : CHECK(z);
778 : const v8::HeapGraphNode* map =
779 5 : GetProperty(env->GetIsolate(), z, v8::HeapGraphEdge::kInternal, "map");
780 5 : CHECK(map);
781 5 : CHECK(
782 : GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "map"));
783 5 : CHECK(GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal,
784 : "prototype"));
785 5 : const v8::HeapGraphNode* parent_map = GetProperty(
786 5 : env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "back_pointer");
787 5 : CHECK(parent_map);
788 :
789 5 : CHECK(GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal,
790 : "back_pointer"));
791 5 : CHECK(GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal,
792 : "descriptors"));
793 5 : CHECK(GetProperty(env->GetIsolate(), parent_map, v8::HeapGraphEdge::kWeak,
794 : "transition"));
795 5 : }
796 :
797 26644 : TEST(HeapSnapshotInternalReferences) {
798 5 : v8::Isolate* isolate = CcTest::isolate();
799 10 : v8::HandleScope scope(isolate);
800 : v8::Local<v8::ObjectTemplate> global_template =
801 5 : v8::ObjectTemplate::New(isolate);
802 5 : global_template->SetInternalFieldCount(2);
803 5 : LocalContext env(nullptr, global_template);
804 5 : v8::Local<v8::Object> global_proxy = env->Global();
805 5 : v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
806 5 : CHECK_EQ(2, global->InternalFieldCount());
807 5 : v8::Local<v8::Object> obj = v8::Object::New(isolate);
808 5 : global->SetInternalField(0, v8_num(17));
809 5 : global->SetInternalField(1, obj);
810 5 : v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
811 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
812 5 : CHECK(ValidateSnapshot(snapshot));
813 5 : const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
814 : // The first reference will not present, because it's a Smi.
815 5 : CHECK(!GetProperty(env->GetIsolate(), global_node,
816 : v8::HeapGraphEdge::kInternal, "0"));
817 : // The second reference is to an object.
818 5 : CHECK(GetProperty(env->GetIsolate(), global_node,
819 : v8::HeapGraphEdge::kInternal, "1"));
820 5 : }
821 :
822 26644 : TEST(HeapSnapshotEphemeron) {
823 5 : LocalContext env;
824 10 : v8::HandleScope scope(env->GetIsolate());
825 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
826 :
827 : CompileRun(
828 : "class KeyClass{};\n"
829 : "class ValueClass{};\n"
830 : "var wm = new WeakMap();\n"
831 : "function foo(key) { wm.set(key, new ValueClass()); }\n"
832 : "var key = new KeyClass();\n"
833 : "foo(key);");
834 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
835 5 : CHECK(ValidateSnapshot(snapshot));
836 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
837 :
838 5 : const v8::HeapGraphNode* key = GetProperty(
839 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "key");
840 5 : CHECK(key);
841 : bool success = false;
842 5 : for (int i = 0, count = key->GetChildrenCount(); i < count; ++i) {
843 5 : const v8::HeapGraphEdge* edge = key->GetChild(i);
844 5 : const v8::HeapGraphNode* child = edge->GetToNode();
845 5 : if (!strcmp("ValueClass", GetName(child))) {
846 10 : v8::String::Utf8Value edge_name(CcTest::isolate(), edge->GetName());
847 5 : CHECK(EndsWith(*edge_name, " / key KeyClass in WeakMap"));
848 : success = true;
849 : break;
850 : }
851 : }
852 5 : CHECK(success);
853 5 : }
854 :
855 26644 : TEST(HeapSnapshotAddressReuse) {
856 5 : LocalContext env;
857 10 : v8::HandleScope scope(env->GetIsolate());
858 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
859 :
860 : CompileRun(
861 : "function A() {}\n"
862 : "var a = [];\n"
863 : "for (var i = 0; i < 10000; ++i)\n"
864 : " a[i] = new A();\n");
865 5 : const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
866 5 : CHECK(ValidateSnapshot(snapshot1));
867 5 : v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId();
868 :
869 : CompileRun(
870 : "for (var i = 0; i < 10000; ++i)\n"
871 : " a[i] = new A();\n");
872 5 : CcTest::CollectAllGarbage();
873 :
874 5 : const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
875 5 : CHECK(ValidateSnapshot(snapshot2));
876 5 : const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
877 :
878 5 : const v8::HeapGraphNode* array_node = GetProperty(
879 5 : env->GetIsolate(), global2, v8::HeapGraphEdge::kProperty, "a");
880 5 : CHECK(array_node);
881 : int wrong_count = 0;
882 50020 : for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) {
883 50015 : const v8::HeapGraphEdge* prop = array_node->GetChild(i);
884 50015 : if (prop->GetType() != v8::HeapGraphEdge::kElement)
885 : continue;
886 50000 : v8::SnapshotObjectId id = prop->GetToNode()->GetId();
887 50000 : if (id < maxId1)
888 0 : ++wrong_count;
889 : }
890 5 : CHECK_EQ(0, wrong_count);
891 5 : }
892 :
893 :
894 26644 : TEST(HeapEntryIdsAndArrayShift) {
895 5 : LocalContext env;
896 10 : v8::HandleScope scope(env->GetIsolate());
897 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
898 :
899 : CompileRun(
900 : "function AnObject() {\n"
901 : " this.first = 'first';\n"
902 : " this.second = 'second';\n"
903 : "}\n"
904 : "var a = new Array();\n"
905 : "for (var i = 0; i < 10; ++i)\n"
906 : " a.push(new AnObject());\n");
907 5 : const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
908 5 : CHECK(ValidateSnapshot(snapshot1));
909 :
910 : CompileRun(
911 : "for (var i = 0; i < 1; ++i)\n"
912 : " a.shift();\n");
913 :
914 5 : CcTest::CollectAllGarbage();
915 :
916 5 : const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
917 5 : CHECK(ValidateSnapshot(snapshot2));
918 :
919 5 : const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
920 5 : const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
921 5 : CHECK_NE(0u, global1->GetId());
922 5 : CHECK_EQ(global1->GetId(), global2->GetId());
923 :
924 5 : const v8::HeapGraphNode* a1 = GetProperty(env->GetIsolate(), global1,
925 5 : v8::HeapGraphEdge::kProperty, "a");
926 5 : CHECK(a1);
927 5 : const v8::HeapGraphNode* k1 = GetProperty(
928 5 : env->GetIsolate(), a1, v8::HeapGraphEdge::kInternal, "elements");
929 5 : CHECK(k1);
930 5 : const v8::HeapGraphNode* a2 = GetProperty(env->GetIsolate(), global2,
931 5 : v8::HeapGraphEdge::kProperty, "a");
932 5 : CHECK(a2);
933 5 : const v8::HeapGraphNode* k2 = GetProperty(
934 5 : env->GetIsolate(), a2, v8::HeapGraphEdge::kInternal, "elements");
935 5 : CHECK(k2);
936 :
937 5 : CHECK_EQ(a1->GetId(), a2->GetId());
938 5 : CHECK_EQ(k1->GetId(), k2->GetId());
939 5 : }
940 :
941 :
942 26644 : TEST(HeapEntryIdsAndGC) {
943 5 : LocalContext env;
944 10 : v8::HandleScope scope(env->GetIsolate());
945 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
946 :
947 : CompileRun(
948 : "function A() {}\n"
949 : "function B(x) { this.x = x; }\n"
950 : "var a = new A();\n"
951 : "var b = new B(a);");
952 5 : const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
953 5 : CHECK(ValidateSnapshot(snapshot1));
954 :
955 5 : CcTest::CollectAllGarbage();
956 :
957 5 : const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
958 5 : CHECK(ValidateSnapshot(snapshot2));
959 :
960 5 : CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000u);
961 5 : CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
962 : snapshot2->GetMaxSnapshotJSObjectId());
963 :
964 5 : const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
965 5 : const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
966 5 : CHECK_NE(0u, global1->GetId());
967 5 : CHECK_EQ(global1->GetId(), global2->GetId());
968 5 : const v8::HeapGraphNode* A1 = GetProperty(env->GetIsolate(), global1,
969 5 : v8::HeapGraphEdge::kProperty, "A");
970 5 : CHECK(A1);
971 5 : const v8::HeapGraphNode* A2 = GetProperty(env->GetIsolate(), global2,
972 5 : v8::HeapGraphEdge::kProperty, "A");
973 5 : CHECK(A2);
974 5 : CHECK_NE(0u, A1->GetId());
975 5 : CHECK_EQ(A1->GetId(), A2->GetId());
976 5 : const v8::HeapGraphNode* B1 = GetProperty(env->GetIsolate(), global1,
977 5 : v8::HeapGraphEdge::kProperty, "B");
978 5 : CHECK(B1);
979 5 : const v8::HeapGraphNode* B2 = GetProperty(env->GetIsolate(), global2,
980 5 : v8::HeapGraphEdge::kProperty, "B");
981 5 : CHECK(B2);
982 5 : CHECK_NE(0u, B1->GetId());
983 5 : CHECK_EQ(B1->GetId(), B2->GetId());
984 5 : const v8::HeapGraphNode* a1 = GetProperty(env->GetIsolate(), global1,
985 5 : v8::HeapGraphEdge::kProperty, "a");
986 5 : CHECK(a1);
987 5 : const v8::HeapGraphNode* a2 = GetProperty(env->GetIsolate(), global2,
988 5 : v8::HeapGraphEdge::kProperty, "a");
989 5 : CHECK(a2);
990 5 : CHECK_NE(0u, a1->GetId());
991 5 : CHECK_EQ(a1->GetId(), a2->GetId());
992 5 : const v8::HeapGraphNode* b1 = GetProperty(env->GetIsolate(), global1,
993 5 : v8::HeapGraphEdge::kProperty, "b");
994 5 : CHECK(b1);
995 5 : const v8::HeapGraphNode* b2 = GetProperty(env->GetIsolate(), global2,
996 5 : v8::HeapGraphEdge::kProperty, "b");
997 5 : CHECK(b2);
998 5 : CHECK_NE(0u, b1->GetId());
999 5 : CHECK_EQ(b1->GetId(), b2->GetId());
1000 5 : }
1001 :
1002 : namespace {
1003 :
1004 : class TestJSONStream : public v8::OutputStream {
1005 : public:
1006 15 : TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
1007 : explicit TestJSONStream(int abort_countdown)
1008 10 : : eos_signaled_(0), abort_countdown_(abort_countdown) {}
1009 20 : ~TestJSONStream() override = default;
1010 5 : void EndOfStream() override { ++eos_signaled_; }
1011 2974 : WriteResult WriteAsciiChunk(char* buffer, int chars_written) override {
1012 2974 : if (abort_countdown_ > 0) --abort_countdown_;
1013 2974 : if (abort_countdown_ == 0) return kAbort;
1014 2969 : CHECK_GT(chars_written, 0);
1015 2969 : i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
1016 2969 : i::MemCopy(chunk.start(), buffer, chars_written);
1017 2969 : return kContinue;
1018 : }
1019 0 : virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
1020 0 : UNREACHABLE();
1021 : }
1022 5 : void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
1023 : int eos_signaled() { return eos_signaled_; }
1024 : int size() { return buffer_.size(); }
1025 :
1026 : private:
1027 : i::Collector<char> buffer_;
1028 : int eos_signaled_;
1029 : int abort_countdown_;
1030 : };
1031 :
1032 10 : class OneByteResource : public v8::String::ExternalOneByteStringResource {
1033 : public:
1034 5 : explicit OneByteResource(i::Vector<char> string) : data_(string.start()) {
1035 5 : length_ = string.length();
1036 : }
1037 3109646 : const char* data() const override { return data_; }
1038 20 : size_t length() const override { return length_; }
1039 :
1040 : private:
1041 : const char* data_;
1042 : size_t length_;
1043 : };
1044 :
1045 : } // namespace
1046 :
1047 26644 : TEST(HeapSnapshotJSONSerialization) {
1048 5 : v8::Isolate* isolate = CcTest::isolate();
1049 5 : LocalContext env;
1050 10 : v8::HandleScope scope(isolate);
1051 5 : v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1052 :
1053 : #define STRING_LITERAL_FOR_TEST \
1054 : "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
1055 : CompileRun(
1056 : "function A(s) { this.s = s; }\n"
1057 : "function B(x) { this.x = x; }\n"
1058 : "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
1059 : "var b = new B(a);");
1060 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1061 5 : CHECK(ValidateSnapshot(snapshot));
1062 :
1063 5 : TestJSONStream stream;
1064 5 : snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1065 5 : CHECK_GT(stream.size(), 0);
1066 5 : CHECK_EQ(1, stream.eos_signaled());
1067 : i::ScopedVector<char> json(stream.size());
1068 : stream.WriteTo(json);
1069 :
1070 : // Verify that snapshot string is valid JSON.
1071 5 : OneByteResource* json_res = new OneByteResource(json);
1072 : v8::Local<v8::String> json_string =
1073 5 : v8::String::NewExternalOneByte(env->GetIsolate(), json_res)
1074 : .ToLocalChecked();
1075 10 : env->Global()
1076 20 : ->Set(env.local(), v8_str("json_snapshot"), json_string)
1077 : .FromJust();
1078 : v8::Local<v8::Value> snapshot_parse_result = CompileRun(
1079 : "var parsed = JSON.parse(json_snapshot); true;");
1080 5 : CHECK(!snapshot_parse_result.IsEmpty());
1081 :
1082 : // Verify that snapshot object has required fields.
1083 : v8::Local<v8::Object> parsed_snapshot =
1084 10 : env->Global()
1085 15 : ->Get(env.local(), v8_str("parsed"))
1086 : .ToLocalChecked()
1087 5 : ->ToObject(env.local())
1088 : .ToLocalChecked();
1089 15 : CHECK(parsed_snapshot->Has(env.local(), v8_str("snapshot")).FromJust());
1090 15 : CHECK(parsed_snapshot->Has(env.local(), v8_str("nodes")).FromJust());
1091 15 : CHECK(parsed_snapshot->Has(env.local(), v8_str("edges")).FromJust());
1092 15 : CHECK(parsed_snapshot->Has(env.local(), v8_str("locations")).FromJust());
1093 15 : CHECK(parsed_snapshot->Has(env.local(), v8_str("strings")).FromJust());
1094 :
1095 : // Get node and edge "member" offsets.
1096 : v8::Local<v8::Value> meta_analysis_result = CompileRun(
1097 : "var meta = parsed.snapshot.meta;\n"
1098 : "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n"
1099 : "var node_fields_count = meta.node_fields.length;\n"
1100 : "var edge_fields_count = meta.edge_fields.length;\n"
1101 : "var edge_type_offset = meta.edge_fields.indexOf('type');\n"
1102 : "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
1103 : "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
1104 : "var property_type ="
1105 : " meta.edge_types[edge_type_offset].indexOf('property');\n"
1106 : "var shortcut_type ="
1107 : " meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
1108 : "var node_count = parsed.nodes.length / node_fields_count;\n"
1109 : "var first_edge_indexes = parsed.first_edge_indexes = [];\n"
1110 : "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n"
1111 : " first_edge_indexes[i] = first_edge_index;\n"
1112 : " first_edge_index += edge_fields_count *\n"
1113 : " parsed.nodes[i * node_fields_count + edge_count_offset];\n"
1114 : "}\n"
1115 : "first_edge_indexes[node_count] = first_edge_index;\n");
1116 5 : CHECK(!meta_analysis_result.IsEmpty());
1117 :
1118 : // A helper function for processing encoded nodes.
1119 : CompileRun(
1120 : "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
1121 : " var nodes = parsed.nodes;\n"
1122 : " var edges = parsed.edges;\n"
1123 : " var strings = parsed.strings;\n"
1124 : " var node_ordinal = pos / node_fields_count;\n"
1125 : " for (var i = parsed.first_edge_indexes[node_ordinal],\n"
1126 : " count = parsed.first_edge_indexes[node_ordinal + 1];\n"
1127 : " i < count; i += edge_fields_count) {\n"
1128 : " if (edges[i + edge_type_offset] === prop_type\n"
1129 : " && strings[edges[i + edge_name_offset]] === prop_name)\n"
1130 : " return edges[i + edge_to_node_offset];\n"
1131 : " }\n"
1132 : " return null;\n"
1133 : "}\n");
1134 : // Get the string index using the path: <root> -> <global>.b.x.s
1135 : v8::Local<v8::Value> string_obj_pos_val = CompileRun(
1136 : "GetChildPosByProperty(\n"
1137 : " GetChildPosByProperty(\n"
1138 : " GetChildPosByProperty("
1139 : " parsed.edges[edge_fields_count + edge_to_node_offset],"
1140 : " \"b\", property_type),\n"
1141 : " \"x\", property_type),"
1142 : " \"s\", property_type)");
1143 5 : CHECK(!string_obj_pos_val.IsEmpty());
1144 : int string_obj_pos = static_cast<int>(
1145 10 : string_obj_pos_val->ToNumber(env.local()).ToLocalChecked()->Value());
1146 : v8::Local<v8::Object> nodes_array =
1147 15 : parsed_snapshot->Get(env.local(), v8_str("nodes"))
1148 : .ToLocalChecked()
1149 5 : ->ToObject(env.local())
1150 : .ToLocalChecked();
1151 : int string_index =
1152 10 : static_cast<int>(nodes_array->Get(env.local(), string_obj_pos + 1)
1153 : .ToLocalChecked()
1154 5 : ->ToNumber(env.local())
1155 : .ToLocalChecked()
1156 5 : ->Value());
1157 5 : CHECK_GT(string_index, 0);
1158 : v8::Local<v8::Object> strings_array =
1159 15 : parsed_snapshot->Get(env.local(), v8_str("strings"))
1160 : .ToLocalChecked()
1161 5 : ->ToObject(env.local())
1162 : .ToLocalChecked();
1163 10 : v8::Local<v8::String> string = strings_array->Get(env.local(), string_index)
1164 : .ToLocalChecked()
1165 5 : ->ToString(env.local())
1166 : .ToLocalChecked();
1167 : v8::Local<v8::String> ref_string = CompileRun(STRING_LITERAL_FOR_TEST)
1168 5 : ->ToString(env.local())
1169 : .ToLocalChecked();
1170 : #undef STRING_LITERAL_FOR_TEST
1171 10 : CHECK_EQ(0, strcmp(*v8::String::Utf8Value(env->GetIsolate(), ref_string),
1172 : *v8::String::Utf8Value(env->GetIsolate(), string)));
1173 5 : }
1174 :
1175 :
1176 26644 : TEST(HeapSnapshotJSONSerializationAborting) {
1177 5 : LocalContext env;
1178 10 : v8::HandleScope scope(env->GetIsolate());
1179 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1180 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1181 5 : CHECK(ValidateSnapshot(snapshot));
1182 : TestJSONStream stream(5);
1183 5 : snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1184 5 : CHECK_GT(stream.size(), 0);
1185 5 : CHECK_EQ(0, stream.eos_signaled());
1186 5 : }
1187 :
1188 : namespace {
1189 :
1190 : class TestStatsStream : public v8::OutputStream {
1191 : public:
1192 : TestStatsStream()
1193 : : eos_signaled_(0),
1194 : updates_written_(0),
1195 : entries_count_(0),
1196 : entries_size_(0),
1197 : intervals_count_(0),
1198 55 : first_interval_index_(-1) { }
1199 : TestStatsStream(const TestStatsStream& stream) V8_NOEXCEPT = default;
1200 110 : ~TestStatsStream() override = default;
1201 55 : void EndOfStream() override { ++eos_signaled_; }
1202 0 : WriteResult WriteAsciiChunk(char* buffer, int chars_written) override {
1203 0 : UNREACHABLE();
1204 : }
1205 45 : WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
1206 : int updates_written) override {
1207 45 : ++intervals_count_;
1208 45 : CHECK(updates_written);
1209 45 : updates_written_ += updates_written;
1210 45 : entries_count_ = 0;
1211 45 : if (first_interval_index_ == -1 && updates_written != 0)
1212 45 : first_interval_index_ = buffer[0].index;
1213 145 : for (int i = 0; i < updates_written; ++i) {
1214 50 : entries_count_ += buffer[i].count;
1215 50 : entries_size_ += buffer[i].size;
1216 : }
1217 :
1218 45 : return kContinue;
1219 : }
1220 : int eos_signaled() { return eos_signaled_; }
1221 : int updates_written() { return updates_written_; }
1222 : uint32_t entries_count() const { return entries_count_; }
1223 : uint32_t entries_size() const { return entries_size_; }
1224 : int intervals_count() const { return intervals_count_; }
1225 : int first_interval_index() const { return first_interval_index_; }
1226 :
1227 : private:
1228 : int eos_signaled_;
1229 : int updates_written_;
1230 : uint32_t entries_count_;
1231 : uint32_t entries_size_;
1232 : int intervals_count_;
1233 : int first_interval_index_;
1234 : };
1235 :
1236 : } // namespace
1237 :
1238 55 : static TestStatsStream GetHeapStatsUpdate(
1239 : v8::HeapProfiler* heap_profiler,
1240 : v8::SnapshotObjectId* object_id = nullptr) {
1241 : TestStatsStream stream;
1242 55 : int64_t timestamp = -1;
1243 : v8::SnapshotObjectId last_seen_id =
1244 55 : heap_profiler->GetHeapStats(&stream, ×tamp);
1245 55 : if (object_id)
1246 20 : *object_id = last_seen_id;
1247 55 : CHECK_NE(-1, timestamp);
1248 55 : CHECK_EQ(1, stream.eos_signaled());
1249 55 : return stream;
1250 : }
1251 :
1252 :
1253 26644 : TEST(HeapSnapshotObjectsStats) {
1254 5 : LocalContext env;
1255 10 : v8::HandleScope scope(env->GetIsolate());
1256 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1257 :
1258 5 : heap_profiler->StartTrackingHeapObjects();
1259 : // We have to call GC 6 times. In other case the garbage will be
1260 : // the reason of flakiness.
1261 65 : for (int i = 0; i < 6; ++i) {
1262 30 : CcTest::CollectAllGarbage();
1263 : }
1264 :
1265 : v8::SnapshotObjectId initial_id;
1266 : {
1267 : // Single chunk of data expected in update. Initial data.
1268 : TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
1269 5 : &initial_id);
1270 5 : CHECK_EQ(1, stats_update.intervals_count());
1271 5 : CHECK_EQ(1, stats_update.updates_written());
1272 5 : CHECK_LT(0u, stats_update.entries_size());
1273 5 : CHECK_EQ(0, stats_update.first_interval_index());
1274 : }
1275 :
1276 : // No data expected in update because nothing has happened.
1277 : v8::SnapshotObjectId same_id;
1278 10 : CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written());
1279 5 : CHECK_EQ(initial_id, same_id);
1280 :
1281 : {
1282 : v8::SnapshotObjectId additional_string_id;
1283 10 : v8::HandleScope inner_scope_1(env->GetIsolate());
1284 5 : v8_str("string1");
1285 : {
1286 : // Single chunk of data with one new entry expected in update.
1287 : TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
1288 5 : &additional_string_id);
1289 5 : CHECK_LT(same_id, additional_string_id);
1290 5 : CHECK_EQ(1, stats_update.intervals_count());
1291 5 : CHECK_EQ(1, stats_update.updates_written());
1292 5 : CHECK_LT(0u, stats_update.entries_size());
1293 5 : CHECK_EQ(1u, stats_update.entries_count());
1294 5 : CHECK_EQ(2, stats_update.first_interval_index());
1295 : }
1296 :
1297 : // No data expected in update because nothing happened.
1298 : v8::SnapshotObjectId last_id;
1299 10 : CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written());
1300 5 : CHECK_EQ(additional_string_id, last_id);
1301 :
1302 : {
1303 10 : v8::HandleScope inner_scope_2(env->GetIsolate());
1304 5 : v8_str("string2");
1305 :
1306 : uint32_t entries_size;
1307 : {
1308 10 : v8::HandleScope inner_scope_3(env->GetIsolate());
1309 5 : v8_str("string3");
1310 5 : v8_str("string4");
1311 :
1312 : {
1313 : // Single chunk of data with three new entries expected in update.
1314 5 : TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1315 5 : CHECK_EQ(1, stats_update.intervals_count());
1316 5 : CHECK_EQ(1, stats_update.updates_written());
1317 5 : CHECK_LT(0u, entries_size = stats_update.entries_size());
1318 5 : CHECK_EQ(3u, stats_update.entries_count());
1319 5 : CHECK_EQ(4, stats_update.first_interval_index());
1320 : }
1321 : }
1322 :
1323 : {
1324 : // Single chunk of data with two left entries expected in update.
1325 5 : TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1326 5 : CHECK_EQ(1, stats_update.intervals_count());
1327 5 : CHECK_EQ(1, stats_update.updates_written());
1328 5 : CHECK_GT(entries_size, stats_update.entries_size());
1329 5 : CHECK_EQ(1u, stats_update.entries_count());
1330 : // Two strings from forth interval were released.
1331 5 : CHECK_EQ(4, stats_update.first_interval_index());
1332 : }
1333 : }
1334 :
1335 : {
1336 : // Single chunk of data with 0 left entries expected in update.
1337 5 : TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1338 5 : CHECK_EQ(1, stats_update.intervals_count());
1339 5 : CHECK_EQ(1, stats_update.updates_written());
1340 5 : CHECK_EQ(0u, stats_update.entries_size());
1341 5 : CHECK_EQ(0u, stats_update.entries_count());
1342 : // The last string from forth interval was released.
1343 5 : CHECK_EQ(4, stats_update.first_interval_index());
1344 : }
1345 : }
1346 : {
1347 : // Single chunk of data with 0 left entries expected in update.
1348 5 : TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1349 5 : CHECK_EQ(1, stats_update.intervals_count());
1350 5 : CHECK_EQ(1, stats_update.updates_written());
1351 5 : CHECK_EQ(0u, stats_update.entries_size());
1352 5 : CHECK_EQ(0u, stats_update.entries_count());
1353 : // The only string from the second interval was released.
1354 5 : CHECK_EQ(2, stats_update.first_interval_index());
1355 : }
1356 :
1357 5 : v8::Local<v8::Array> array = v8::Array::New(env->GetIsolate());
1358 5 : CHECK_EQ(0u, array->Length());
1359 : // Force array's buffer allocation.
1360 15 : array->Set(env.local(), 2, v8_num(7)).FromJust();
1361 :
1362 : uint32_t entries_size;
1363 : {
1364 : // Single chunk of data with 2 entries expected in update.
1365 5 : TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1366 5 : CHECK_EQ(1, stats_update.intervals_count());
1367 5 : CHECK_EQ(1, stats_update.updates_written());
1368 5 : CHECK_LT(0u, entries_size = stats_update.entries_size());
1369 : // They are the array and its buffer.
1370 5 : CHECK_EQ(2u, stats_update.entries_count());
1371 5 : CHECK_EQ(8, stats_update.first_interval_index());
1372 : }
1373 :
1374 1005 : for (int i = 0; i < 100; ++i)
1375 1500 : array->Set(env.local(), i, v8_num(i)).FromJust();
1376 :
1377 : {
1378 : // Single chunk of data with 1 entry expected in update.
1379 5 : TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1380 5 : CHECK_EQ(1, stats_update.intervals_count());
1381 : // The first interval was changed because old buffer was collected.
1382 : // The second interval was changed because new buffer was allocated.
1383 5 : CHECK_EQ(2, stats_update.updates_written());
1384 5 : CHECK_LT(entries_size, stats_update.entries_size());
1385 5 : CHECK_EQ(2u, stats_update.entries_count());
1386 5 : CHECK_EQ(8, stats_update.first_interval_index());
1387 : }
1388 :
1389 5 : heap_profiler->StopTrackingHeapObjects();
1390 5 : }
1391 :
1392 :
1393 26644 : TEST(HeapObjectIds) {
1394 5 : LocalContext env;
1395 5 : v8::Isolate* isolate = env->GetIsolate();
1396 10 : v8::HandleScope scope(isolate);
1397 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1398 :
1399 : const int kLength = 10;
1400 105 : v8::Local<v8::Object> objects[kLength];
1401 : v8::SnapshotObjectId ids[kLength];
1402 :
1403 5 : heap_profiler->StartTrackingHeapObjects(false);
1404 :
1405 105 : for (int i = 0; i < kLength; i++) {
1406 50 : objects[i] = v8::Object::New(isolate);
1407 : }
1408 10 : GetHeapStatsUpdate(heap_profiler);
1409 :
1410 105 : for (int i = 0; i < kLength; i++) {
1411 50 : v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1412 50 : CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
1413 50 : ids[i] = id;
1414 : }
1415 :
1416 5 : heap_profiler->StopTrackingHeapObjects();
1417 5 : CcTest::CollectAllAvailableGarbage();
1418 :
1419 105 : for (int i = 0; i < kLength; i++) {
1420 50 : v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1421 50 : CHECK_EQ(ids[i], id);
1422 50 : v8::Local<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
1423 100 : CHECK(objects[i]->Equals(env.local(), obj).FromJust());
1424 : }
1425 :
1426 5 : heap_profiler->ClearObjectIds();
1427 105 : for (int i = 0; i < kLength; i++) {
1428 50 : v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1429 50 : CHECK_EQ(v8::HeapProfiler::kUnknownObjectId, id);
1430 50 : v8::Local<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
1431 50 : CHECK(obj.IsEmpty());
1432 : }
1433 5 : }
1434 :
1435 :
1436 159916 : static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
1437 : const v8::HeapGraphNode* node,
1438 : int level, int max_level) {
1439 159916 : if (level > max_level) return;
1440 23368 : CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
1441 183279 : for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
1442 159911 : const v8::HeapGraphEdge* prop = node->GetChild(i);
1443 : const v8::HeapGraphNode* child =
1444 159911 : snapshot->GetNodeById(prop->GetToNode()->GetId());
1445 159911 : CHECK_EQ(prop->GetToNode()->GetId(), child->GetId());
1446 159911 : CHECK_EQ(prop->GetToNode(), child);
1447 159911 : CheckChildrenIds(snapshot, child, level + 1, max_level);
1448 : }
1449 : }
1450 :
1451 :
1452 26644 : TEST(HeapSnapshotGetNodeById) {
1453 5 : LocalContext env;
1454 10 : v8::HandleScope scope(env->GetIsolate());
1455 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1456 :
1457 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1458 5 : CHECK(ValidateSnapshot(snapshot));
1459 5 : const v8::HeapGraphNode* root = snapshot->GetRoot();
1460 5 : CheckChildrenIds(snapshot, root, 0, 3);
1461 : // Check a big id, which should not exist yet.
1462 5 : CHECK(!snapshot->GetNodeById(0x1000000UL));
1463 5 : }
1464 :
1465 :
1466 26644 : TEST(HeapSnapshotGetSnapshotObjectId) {
1467 5 : LocalContext env;
1468 10 : v8::HandleScope scope(env->GetIsolate());
1469 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1470 : CompileRun("globalObject = {};\n");
1471 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1472 5 : CHECK(ValidateSnapshot(snapshot));
1473 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1474 5 : const v8::HeapGraphNode* global_object = GetProperty(
1475 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "globalObject");
1476 5 : CHECK(global_object);
1477 :
1478 : v8::Local<v8::Value> globalObjectHandle =
1479 20 : env->Global()->Get(env.local(), v8_str("globalObject")).ToLocalChecked();
1480 5 : CHECK(!globalObjectHandle.IsEmpty());
1481 5 : CHECK(globalObjectHandle->IsObject());
1482 :
1483 5 : v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle);
1484 5 : CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
1485 5 : CHECK_EQ(id, global_object->GetId());
1486 5 : }
1487 :
1488 :
1489 26644 : TEST(HeapSnapshotUnknownSnapshotObjectId) {
1490 5 : LocalContext env;
1491 10 : v8::HandleScope scope(env->GetIsolate());
1492 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1493 : CompileRun("globalObject = {};\n");
1494 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1495 5 : CHECK(ValidateSnapshot(snapshot));
1496 : const v8::HeapGraphNode* node =
1497 5 : snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
1498 5 : CHECK(!node);
1499 5 : }
1500 :
1501 :
1502 : namespace {
1503 :
1504 25 : class TestActivityControl : public v8::ActivityControl {
1505 : public:
1506 : explicit TestActivityControl(int abort_count)
1507 : : done_(0),
1508 : total_(0),
1509 : abort_count_(abort_count),
1510 15 : reported_finish_(false) {}
1511 35 : ControlOption ReportProgressValue(int done, int total) override {
1512 35 : done_ = done;
1513 35 : total_ = total;
1514 35 : CHECK_LE(done_, total_);
1515 35 : if (done_ == total_) {
1516 10 : CHECK(!reported_finish_);
1517 10 : reported_finish_ = true;
1518 : }
1519 35 : return --abort_count_ != 0 ? kContinue : kAbort;
1520 : }
1521 : int done() { return done_; }
1522 : int total() { return total_; }
1523 :
1524 : private:
1525 : int done_;
1526 : int total_;
1527 : int abort_count_;
1528 : bool reported_finish_;
1529 : };
1530 :
1531 : } // namespace
1532 :
1533 :
1534 26644 : TEST(TakeHeapSnapshotAborting) {
1535 5 : LocalContext env;
1536 10 : v8::HandleScope scope(env->GetIsolate());
1537 :
1538 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1539 5 : const int snapshots_count = heap_profiler->GetSnapshotCount();
1540 : TestActivityControl aborting_control(1);
1541 : const v8::HeapSnapshot* no_snapshot =
1542 5 : heap_profiler->TakeHeapSnapshot(&aborting_control);
1543 5 : CHECK(!no_snapshot);
1544 5 : CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount());
1545 5 : CHECK_GT(aborting_control.total(), aborting_control.done());
1546 :
1547 : TestActivityControl control(-1); // Don't abort.
1548 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(&control);
1549 5 : CHECK(ValidateSnapshot(snapshot));
1550 :
1551 5 : CHECK(snapshot);
1552 5 : CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount());
1553 5 : CHECK_EQ(control.total(), control.done());
1554 5 : CHECK_GT(control.total(), 0);
1555 5 : }
1556 :
1557 26644 : TEST(TakeHeapSnapshotReportFinishOnce) {
1558 5 : LocalContext env;
1559 10 : v8::HandleScope scope(env->GetIsolate());
1560 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1561 : TestActivityControl control(-1);
1562 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(&control);
1563 5 : CHECK(ValidateSnapshot(snapshot));
1564 5 : CHECK_EQ(control.total(), control.done());
1565 5 : CHECK_GT(control.total(), 0);
1566 5 : }
1567 :
1568 : namespace {
1569 :
1570 5 : class EmbedderGraphBuilder : public v8::PersistentHandleVisitor {
1571 : public:
1572 20 : class Node : public v8::EmbedderGraph::Node {
1573 : public:
1574 10 : Node(const char* name, size_t size) : name_(name), size_(size) {}
1575 : // v8::EmbedderGraph::Node
1576 10 : const char* Name() override { return name_; }
1577 10 : size_t SizeInBytes() override { return size_; }
1578 :
1579 : private:
1580 : const char* name_;
1581 : size_t size_;
1582 : };
1583 :
1584 20 : class Group : public Node {
1585 : public:
1586 10 : explicit Group(const char* name) : Node(name, 0) {}
1587 : // v8::EmbedderGraph::EmbedderNode
1588 20 : bool IsRootNode() override { return true; }
1589 : };
1590 :
1591 5 : EmbedderGraphBuilder(v8::Isolate* isolate, v8::EmbedderGraph* graph)
1592 5 : : isolate_(isolate), graph_(graph) {
1593 5 : classid_to_group_[0] = nullptr;
1594 : classid_to_group_[1] =
1595 15 : graph->AddNode(std::unique_ptr<Group>(new Group("aaa-group")));
1596 : classid_to_group_[2] =
1597 15 : graph->AddNode(std::unique_ptr<Group>(new Group("ccc-group")));
1598 5 : }
1599 :
1600 5 : static void BuildEmbedderGraph(v8::Isolate* isolate, v8::EmbedderGraph* graph,
1601 : void* data) {
1602 5 : EmbedderGraphBuilder builder(isolate, graph);
1603 5 : isolate->VisitHandlesWithClassIds(&builder);
1604 5 : }
1605 :
1606 15 : void VisitPersistentHandle(v8::Persistent<v8::Value>* value,
1607 : uint16_t class_id) override {
1608 : v8::Local<v8::Value> wrapper = v8::Local<v8::Value>::New(
1609 30 : isolate_, v8::Persistent<v8::Value>::Cast(*value));
1610 15 : if (class_id == 1) {
1611 10 : if (wrapper->IsString()) {
1612 20 : v8::String::Utf8Value utf8(CcTest::isolate(), wrapper);
1613 : DCHECK(!strcmp(*utf8, "AAA") || !strcmp(*utf8, "BBB"));
1614 10 : v8::EmbedderGraph::Node* node = graph_->V8Node(wrapper);
1615 10 : v8::EmbedderGraph::Node* group = classid_to_group_[1];
1616 10 : graph_->AddEdge(node, group);
1617 10 : graph_->AddEdge(group, node);
1618 : }
1619 5 : } else if (class_id == 2) {
1620 5 : if (wrapper->IsString()) {
1621 10 : v8::String::Utf8Value utf8(CcTest::isolate(), wrapper);
1622 : DCHECK(!strcmp(*utf8, "CCC"));
1623 5 : v8::EmbedderGraph::Node* node = graph_->V8Node(wrapper);
1624 5 : v8::EmbedderGraph::Node* group = classid_to_group_[2];
1625 5 : graph_->AddEdge(node, group);
1626 5 : graph_->AddEdge(group, node);
1627 : }
1628 : } else {
1629 0 : UNREACHABLE();
1630 : }
1631 15 : }
1632 :
1633 : private:
1634 : v8::Isolate* isolate_;
1635 : v8::EmbedderGraph* graph_;
1636 : v8::EmbedderGraph::Node* classid_to_group_[3];
1637 : };
1638 :
1639 : } // namespace
1640 :
1641 :
1642 55 : static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
1643 : v8::HeapGraphNode::Type type,
1644 : const char* name) {
1645 230 : for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
1646 230 : const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
1647 230 : if (node->GetType() == type && strcmp(name,
1648 : const_cast<i::HeapEntry*>(
1649 : reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
1650 : return node;
1651 : }
1652 : }
1653 : return nullptr;
1654 : }
1655 :
1656 :
1657 26644 : TEST(HeapSnapshotRetainedObjectInfo) {
1658 5 : LocalContext env;
1659 5 : v8::Isolate* isolate = env->GetIsolate();
1660 10 : v8::HandleScope scope(isolate);
1661 5 : v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1662 :
1663 : heap_profiler->AddBuildEmbedderGraphCallback(
1664 5 : EmbedderGraphBuilder::BuildEmbedderGraph, nullptr);
1665 5 : v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
1666 : p_AAA.SetWrapperClassId(1);
1667 5 : v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
1668 : p_BBB.SetWrapperClassId(1);
1669 5 : v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC"));
1670 : p_CCC.SetWrapperClassId(2);
1671 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1672 5 : CHECK(ValidateSnapshot(snapshot));
1673 :
1674 : const v8::HeapGraphNode* native_group_aaa =
1675 5 : GetNode(snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
1676 5 : CHECK_NOT_NULL(native_group_aaa);
1677 : const v8::HeapGraphNode* native_group_ccc =
1678 5 : GetNode(snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
1679 5 : CHECK_NOT_NULL(native_group_ccc);
1680 :
1681 : const v8::HeapGraphNode* n_AAA =
1682 5 : GetNode(native_group_aaa, v8::HeapGraphNode::kString, "AAA");
1683 5 : CHECK(n_AAA);
1684 : const v8::HeapGraphNode* n_BBB =
1685 5 : GetNode(native_group_aaa, v8::HeapGraphNode::kString, "BBB");
1686 5 : CHECK(n_BBB);
1687 : const v8::HeapGraphNode* n_CCC =
1688 5 : GetNode(native_group_ccc, v8::HeapGraphNode::kString, "CCC");
1689 5 : CHECK(n_CCC);
1690 :
1691 5 : CHECK_EQ(native_group_aaa, GetChildByName(n_AAA, "aaa-group"));
1692 5 : CHECK_EQ(native_group_aaa, GetChildByName(n_BBB, "aaa-group"));
1693 5 : CHECK_EQ(native_group_ccc, GetChildByName(n_CCC, "ccc-group"));
1694 5 : }
1695 :
1696 26644 : TEST(DeleteAllHeapSnapshots) {
1697 5 : LocalContext env;
1698 10 : v8::HandleScope scope(env->GetIsolate());
1699 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1700 :
1701 5 : CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1702 5 : heap_profiler->DeleteAllHeapSnapshots();
1703 5 : CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1704 5 : CHECK(heap_profiler->TakeHeapSnapshot());
1705 5 : CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1706 5 : heap_profiler->DeleteAllHeapSnapshots();
1707 5 : CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1708 5 : CHECK(heap_profiler->TakeHeapSnapshot());
1709 5 : CHECK(heap_profiler->TakeHeapSnapshot());
1710 5 : CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1711 5 : heap_profiler->DeleteAllHeapSnapshots();
1712 5 : CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1713 5 : }
1714 :
1715 :
1716 35 : static bool FindHeapSnapshot(v8::HeapProfiler* profiler,
1717 : const v8::HeapSnapshot* snapshot) {
1718 35 : int length = profiler->GetSnapshotCount();
1719 55 : for (int i = 0; i < length; i++) {
1720 30 : if (snapshot == profiler->GetHeapSnapshot(i)) return true;
1721 : }
1722 : return false;
1723 : }
1724 :
1725 :
1726 26644 : TEST(DeleteHeapSnapshot) {
1727 5 : LocalContext env;
1728 10 : v8::HandleScope scope(env->GetIsolate());
1729 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1730 :
1731 5 : CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1732 5 : const v8::HeapSnapshot* s1 = heap_profiler->TakeHeapSnapshot();
1733 :
1734 5 : CHECK(s1);
1735 5 : CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1736 5 : CHECK(FindHeapSnapshot(heap_profiler, s1));
1737 5 : const_cast<v8::HeapSnapshot*>(s1)->Delete();
1738 5 : CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1739 5 : CHECK(!FindHeapSnapshot(heap_profiler, s1));
1740 :
1741 5 : const v8::HeapSnapshot* s2 = heap_profiler->TakeHeapSnapshot();
1742 5 : CHECK(s2);
1743 5 : CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1744 5 : CHECK(FindHeapSnapshot(heap_profiler, s2));
1745 5 : const v8::HeapSnapshot* s3 = heap_profiler->TakeHeapSnapshot();
1746 5 : CHECK(s3);
1747 5 : CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1748 5 : CHECK_NE(s2, s3);
1749 5 : CHECK(FindHeapSnapshot(heap_profiler, s3));
1750 5 : const_cast<v8::HeapSnapshot*>(s2)->Delete();
1751 5 : CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1752 5 : CHECK(!FindHeapSnapshot(heap_profiler, s2));
1753 5 : CHECK(FindHeapSnapshot(heap_profiler, s3));
1754 5 : const_cast<v8::HeapSnapshot*>(s3)->Delete();
1755 5 : CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1756 5 : CHECK(!FindHeapSnapshot(heap_profiler, s3));
1757 5 : }
1758 :
1759 :
1760 10 : class NameResolver : public v8::HeapProfiler::ObjectNameResolver {
1761 : public:
1762 5 : const char* GetName(v8::Local<v8::Object> object) override {
1763 5 : return "Global object name";
1764 : }
1765 : };
1766 :
1767 :
1768 26644 : TEST(GlobalObjectName) {
1769 5 : LocalContext env;
1770 10 : v8::HandleScope scope(env->GetIsolate());
1771 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1772 :
1773 : CompileRun("document = { URL:\"abcdefgh\" };");
1774 :
1775 5 : NameResolver name_resolver;
1776 : const v8::HeapSnapshot* snapshot =
1777 5 : heap_profiler->TakeHeapSnapshot(nullptr, &name_resolver);
1778 5 : CHECK(ValidateSnapshot(snapshot));
1779 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1780 5 : CHECK(global);
1781 5 : CHECK_EQ(0,
1782 : strcmp("Object / Global object name",
1783 : const_cast<i::HeapEntry*>(
1784 : reinterpret_cast<const i::HeapEntry*>(global))->name()));
1785 5 : }
1786 :
1787 :
1788 26644 : TEST(GlobalObjectFields) {
1789 5 : LocalContext env;
1790 10 : v8::HandleScope scope(env->GetIsolate());
1791 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1792 : CompileRun("obj = {};");
1793 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1794 5 : CHECK(ValidateSnapshot(snapshot));
1795 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1796 : const v8::HeapGraphNode* native_context =
1797 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kInternal,
1798 5 : "native_context");
1799 5 : CHECK(native_context);
1800 5 : const v8::HeapGraphNode* global_proxy = GetProperty(
1801 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kInternal, "global_proxy");
1802 5 : CHECK(global_proxy);
1803 5 : }
1804 :
1805 :
1806 26644 : TEST(NoHandleLeaks) {
1807 5 : LocalContext env;
1808 10 : v8::HandleScope scope(env->GetIsolate());
1809 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1810 :
1811 : CompileRun("document = { URL:\"abcdefgh\" };");
1812 :
1813 : i::Isolate* isolate = CcTest::i_isolate();
1814 5 : int count_before = i::HandleScope::NumberOfHandles(isolate);
1815 5 : heap_profiler->TakeHeapSnapshot();
1816 5 : int count_after = i::HandleScope::NumberOfHandles(isolate);
1817 5 : CHECK_EQ(count_before, count_after);
1818 5 : }
1819 :
1820 :
1821 26644 : TEST(NodesIteration) {
1822 5 : LocalContext env;
1823 10 : v8::HandleScope scope(env->GetIsolate());
1824 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1825 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1826 5 : CHECK(ValidateSnapshot(snapshot));
1827 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1828 5 : CHECK(global);
1829 : // Verify that we can find this object by iteration.
1830 5 : const int nodes_count = snapshot->GetNodesCount();
1831 : int count = 0;
1832 67341 : for (int i = 0; i < nodes_count; ++i) {
1833 33668 : if (snapshot->GetNode(i) == global)
1834 5 : ++count;
1835 : }
1836 5 : CHECK_EQ(1, count);
1837 5 : }
1838 :
1839 :
1840 26644 : TEST(GetHeapValueForNode) {
1841 5 : LocalContext env;
1842 10 : v8::HandleScope scope(env->GetIsolate());
1843 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1844 :
1845 : CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
1846 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1847 5 : CHECK(ValidateSnapshot(snapshot));
1848 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1849 10 : CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject());
1850 : v8::Local<v8::Object> js_global =
1851 10 : env->Global()->GetPrototype().As<v8::Object>();
1852 10 : CHECK_EQ(js_global, heap_profiler->FindObjectById(global->GetId()));
1853 : const v8::HeapGraphNode* obj =
1854 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
1855 10 : CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject());
1856 15 : v8::Local<v8::Object> js_obj = js_global->Get(env.local(), v8_str("a"))
1857 : .ToLocalChecked()
1858 : .As<v8::Object>();
1859 10 : CHECK_EQ(js_obj, heap_profiler->FindObjectById(obj->GetId()));
1860 5 : const v8::HeapGraphNode* s_prop = GetProperty(
1861 5 : env->GetIsolate(), obj, v8::HeapGraphEdge::kProperty, "s_prop");
1862 15 : v8::Local<v8::String> js_s_prop = js_obj->Get(env.local(), v8_str("s_prop"))
1863 : .ToLocalChecked()
1864 : .As<v8::String>();
1865 10 : CHECK_EQ(js_s_prop, heap_profiler->FindObjectById(s_prop->GetId()));
1866 5 : const v8::HeapGraphNode* n_prop = GetProperty(
1867 5 : env->GetIsolate(), obj, v8::HeapGraphEdge::kProperty, "n_prop");
1868 15 : v8::Local<v8::String> js_n_prop = js_obj->Get(env.local(), v8_str("n_prop"))
1869 : .ToLocalChecked()
1870 : .As<v8::String>();
1871 10 : CHECK_EQ(js_n_prop, heap_profiler->FindObjectById(n_prop->GetId()));
1872 5 : }
1873 :
1874 :
1875 26644 : TEST(GetHeapValueForDeletedObject) {
1876 5 : LocalContext env;
1877 10 : v8::HandleScope scope(env->GetIsolate());
1878 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1879 :
1880 : // It is impossible to delete a global property, so we are about to delete a
1881 : // property of the "a" object. Also, the "p" object can't be an empty one
1882 : // because the empty object is static and isn't actually deleted.
1883 : CompileRun("a = { p: { r: {} } };");
1884 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1885 5 : CHECK(ValidateSnapshot(snapshot));
1886 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1887 : const v8::HeapGraphNode* obj =
1888 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
1889 : const v8::HeapGraphNode* prop =
1890 5 : GetProperty(env->GetIsolate(), obj, v8::HeapGraphEdge::kProperty, "p");
1891 : {
1892 : // Perform the check inside a nested local scope to avoid creating a
1893 : // reference to the object we are deleting.
1894 10 : v8::HandleScope scope(env->GetIsolate());
1895 10 : CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject());
1896 : }
1897 : CompileRun("delete a.p;");
1898 10 : CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty());
1899 5 : }
1900 :
1901 30 : static int StringCmp(const char* ref, i::String act) {
1902 30 : std::unique_ptr<char[]> s_act = act->ToCString();
1903 30 : int result = strcmp(ref, s_act.get());
1904 30 : if (result != 0)
1905 0 : fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get());
1906 30 : return result;
1907 : }
1908 :
1909 26644 : TEST(GetConstructor) {
1910 5 : LocalContext env;
1911 10 : v8::HandleScope scope(env->GetIsolate());
1912 :
1913 : CompileRun(
1914 : "function Constructor1() {};\n"
1915 : "var obj1 = new Constructor1();\n"
1916 : "var Constructor2 = function() {};\n"
1917 : "var obj2 = new Constructor2();\n"
1918 : "var obj3 = {};\n"
1919 : "obj3.__proto__ = { constructor: function Constructor3() {} };\n"
1920 : "var obj4 = {};\n"
1921 : "// Slow properties\n"
1922 : "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1923 : "obj4.__proto__ = { constructor: function Constructor4() {} };\n"
1924 : "var obj5 = {};\n"
1925 : "var obj6 = {};\n"
1926 : "obj6.constructor = 6;");
1927 : v8::Local<v8::Object> js_global =
1928 10 : env->Global()->GetPrototype().As<v8::Object>();
1929 15 : v8::Local<v8::Object> obj1 = js_global->Get(env.local(), v8_str("obj1"))
1930 : .ToLocalChecked()
1931 : .As<v8::Object>();
1932 : i::Handle<i::JSObject> js_obj1 =
1933 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj1));
1934 10 : CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj1).is_null());
1935 15 : v8::Local<v8::Object> obj2 = js_global->Get(env.local(), v8_str("obj2"))
1936 : .ToLocalChecked()
1937 : .As<v8::Object>();
1938 : i::Handle<i::JSObject> js_obj2 =
1939 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj2));
1940 10 : CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj2).is_null());
1941 15 : v8::Local<v8::Object> obj3 = js_global->Get(env.local(), v8_str("obj3"))
1942 : .ToLocalChecked()
1943 : .As<v8::Object>();
1944 : i::Handle<i::JSObject> js_obj3 =
1945 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj3));
1946 10 : CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj3).is_null());
1947 15 : v8::Local<v8::Object> obj4 = js_global->Get(env.local(), v8_str("obj4"))
1948 : .ToLocalChecked()
1949 : .As<v8::Object>();
1950 : i::Handle<i::JSObject> js_obj4 =
1951 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj4));
1952 10 : CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj4).is_null());
1953 15 : v8::Local<v8::Object> obj5 = js_global->Get(env.local(), v8_str("obj5"))
1954 : .ToLocalChecked()
1955 : .As<v8::Object>();
1956 : i::Handle<i::JSObject> js_obj5 =
1957 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj5));
1958 10 : CHECK(i::V8HeapExplorer::GetConstructor(*js_obj5).is_null());
1959 15 : v8::Local<v8::Object> obj6 = js_global->Get(env.local(), v8_str("obj6"))
1960 : .ToLocalChecked()
1961 : .As<v8::Object>();
1962 : i::Handle<i::JSObject> js_obj6 =
1963 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj6));
1964 10 : CHECK(i::V8HeapExplorer::GetConstructor(*js_obj6).is_null());
1965 5 : }
1966 :
1967 26644 : TEST(GetConstructorName) {
1968 5 : LocalContext env;
1969 10 : v8::HandleScope scope(env->GetIsolate());
1970 :
1971 : CompileRun(
1972 : "function Constructor1() {};\n"
1973 : "var obj1 = new Constructor1();\n"
1974 : "var Constructor2 = function() {};\n"
1975 : "var obj2 = new Constructor2();\n"
1976 : "var obj3 = {};\n"
1977 : "obj3.__proto__ = { constructor: function Constructor3() {} };\n"
1978 : "var obj4 = {};\n"
1979 : "// Slow properties\n"
1980 : "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1981 : "obj4.__proto__ = { constructor: function Constructor4() {} };\n"
1982 : "var obj5 = {};\n"
1983 : "var obj6 = {};\n"
1984 : "obj6.constructor = 6;");
1985 : v8::Local<v8::Object> js_global =
1986 10 : env->Global()->GetPrototype().As<v8::Object>();
1987 15 : v8::Local<v8::Object> obj1 = js_global->Get(env.local(), v8_str("obj1"))
1988 : .ToLocalChecked()
1989 : .As<v8::Object>();
1990 : i::Handle<i::JSObject> js_obj1 =
1991 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj1));
1992 5 : CHECK_EQ(0, StringCmp(
1993 : "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
1994 15 : v8::Local<v8::Object> obj2 = js_global->Get(env.local(), v8_str("obj2"))
1995 : .ToLocalChecked()
1996 : .As<v8::Object>();
1997 : i::Handle<i::JSObject> js_obj2 =
1998 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj2));
1999 5 : CHECK_EQ(0, StringCmp(
2000 : "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
2001 15 : v8::Local<v8::Object> obj3 = js_global->Get(env.local(), v8_str("obj3"))
2002 : .ToLocalChecked()
2003 : .As<v8::Object>();
2004 : i::Handle<i::JSObject> js_obj3 =
2005 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj3));
2006 5 : CHECK_EQ(0, StringCmp("Constructor3",
2007 : i::V8HeapExplorer::GetConstructorName(*js_obj3)));
2008 15 : v8::Local<v8::Object> obj4 = js_global->Get(env.local(), v8_str("obj4"))
2009 : .ToLocalChecked()
2010 : .As<v8::Object>();
2011 : i::Handle<i::JSObject> js_obj4 =
2012 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj4));
2013 5 : CHECK_EQ(0, StringCmp("Constructor4",
2014 : i::V8HeapExplorer::GetConstructorName(*js_obj4)));
2015 15 : v8::Local<v8::Object> obj5 = js_global->Get(env.local(), v8_str("obj5"))
2016 : .ToLocalChecked()
2017 : .As<v8::Object>();
2018 : i::Handle<i::JSObject> js_obj5 =
2019 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj5));
2020 5 : CHECK_EQ(0, StringCmp(
2021 : "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
2022 15 : v8::Local<v8::Object> obj6 = js_global->Get(env.local(), v8_str("obj6"))
2023 : .ToLocalChecked()
2024 : .As<v8::Object>();
2025 : i::Handle<i::JSObject> js_obj6 =
2026 : i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj6));
2027 5 : CHECK_EQ(0, StringCmp(
2028 : "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
2029 5 : }
2030 :
2031 :
2032 26644 : TEST(FastCaseAccessors) {
2033 5 : LocalContext env;
2034 10 : v8::HandleScope scope(env->GetIsolate());
2035 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2036 :
2037 : CompileRun("var obj1 = {};\n"
2038 : "obj1.__defineGetter__('propWithGetter', function Y() {\n"
2039 : " return 42;\n"
2040 : "});\n"
2041 : "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
2042 : " return this.value_ = value;\n"
2043 : "});\n");
2044 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2045 5 : CHECK(ValidateSnapshot(snapshot));
2046 :
2047 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2048 5 : CHECK(global);
2049 5 : const v8::HeapGraphNode* obj1 = GetProperty(
2050 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj1");
2051 5 : CHECK(obj1);
2052 : const v8::HeapGraphNode* func;
2053 5 : func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
2054 5 : "get propWithGetter");
2055 5 : CHECK(func);
2056 5 : func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
2057 5 : "set propWithGetter");
2058 5 : CHECK(!func);
2059 5 : func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
2060 5 : "set propWithSetter");
2061 5 : CHECK(func);
2062 5 : func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
2063 5 : "get propWithSetter");
2064 5 : CHECK(!func);
2065 5 : }
2066 :
2067 :
2068 26644 : TEST(FastCaseRedefinedAccessors) {
2069 5 : LocalContext env;
2070 10 : v8::HandleScope scope(env->GetIsolate());
2071 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2072 :
2073 : CompileRun(
2074 : "var obj1 = {};\n"
2075 : "Object.defineProperty(obj1, 'prop', { "
2076 : " get: function() { return 42; },\n"
2077 : " set: function(value) { return this.prop_ = value; },\n"
2078 : " configurable: true,\n"
2079 : " enumerable: true,\n"
2080 : "});\n"
2081 : "Object.defineProperty(obj1, 'prop', { "
2082 : " get: function() { return 153; },\n"
2083 : " set: function(value) { return this.prop_ = value; },\n"
2084 : " configurable: true,\n"
2085 : " enumerable: true,\n"
2086 : "});\n");
2087 : v8::Local<v8::Object> js_global =
2088 10 : env->Global()->GetPrototype().As<v8::Object>();
2089 : i::Handle<i::JSReceiver> js_obj1 =
2090 15 : v8::Utils::OpenHandle(*js_global->Get(env.local(), v8_str("obj1"))
2091 : .ToLocalChecked()
2092 : .As<v8::Object>());
2093 : USE(js_obj1);
2094 :
2095 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2096 5 : CHECK(ValidateSnapshot(snapshot));
2097 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2098 5 : CHECK(global);
2099 5 : const v8::HeapGraphNode* obj1 = GetProperty(
2100 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj1");
2101 5 : CHECK(obj1);
2102 : const v8::HeapGraphNode* func;
2103 5 : func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
2104 5 : "get prop");
2105 5 : CHECK(func);
2106 5 : func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
2107 5 : "set prop");
2108 5 : CHECK(func);
2109 5 : }
2110 :
2111 :
2112 26644 : TEST(SlowCaseAccessors) {
2113 5 : LocalContext env;
2114 10 : v8::HandleScope scope(env->GetIsolate());
2115 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2116 :
2117 : CompileRun("var obj1 = {};\n"
2118 : "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};"
2119 : "obj1.__defineGetter__('propWithGetter', function Y() {\n"
2120 : " return 42;\n"
2121 : "});\n"
2122 : "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
2123 : " return this.value_ = value;\n"
2124 : "});\n");
2125 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2126 5 : CHECK(ValidateSnapshot(snapshot));
2127 :
2128 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2129 5 : CHECK(global);
2130 5 : const v8::HeapGraphNode* obj1 = GetProperty(
2131 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj1");
2132 5 : CHECK(obj1);
2133 : const v8::HeapGraphNode* func;
2134 5 : func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
2135 5 : "get propWithGetter");
2136 5 : CHECK(func);
2137 5 : func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
2138 5 : "set propWithGetter");
2139 5 : CHECK(!func);
2140 5 : func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
2141 5 : "set propWithSetter");
2142 5 : CHECK(func);
2143 5 : func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
2144 5 : "get propWithSetter");
2145 5 : CHECK(!func);
2146 5 : }
2147 :
2148 :
2149 26644 : TEST(HiddenPropertiesFastCase) {
2150 5 : v8::Isolate* isolate = CcTest::isolate();
2151 5 : LocalContext env;
2152 10 : v8::HandleScope scope(isolate);
2153 5 : v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2154 :
2155 : CompileRun(
2156 : "function C(x) { this.a = this; this.b = x; }\n"
2157 : "c = new C(2012);\n");
2158 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2159 5 : CHECK(ValidateSnapshot(snapshot));
2160 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2161 : const v8::HeapGraphNode* c =
2162 5 : GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "c");
2163 5 : CHECK(c);
2164 : const v8::HeapGraphNode* hidden_props =
2165 5 : GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "<symbol>");
2166 5 : CHECK(!hidden_props);
2167 :
2168 : v8::Local<v8::Value> cHandle =
2169 20 : env->Global()->Get(env.local(), v8_str("c")).ToLocalChecked();
2170 5 : CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
2171 5 : cHandle->ToObject(env.local())
2172 : .ToLocalChecked()
2173 10 : ->SetPrivate(env.local(),
2174 : v8::Private::ForApi(env->GetIsolate(), v8_str("key")),
2175 20 : v8_str("val"))
2176 : .FromJust();
2177 :
2178 5 : snapshot = heap_profiler->TakeHeapSnapshot();
2179 5 : CHECK(ValidateSnapshot(snapshot));
2180 5 : global = GetGlobalObject(snapshot);
2181 5 : c = GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "c");
2182 5 : CHECK(c);
2183 : hidden_props =
2184 5 : GetProperty(isolate, c, v8::HeapGraphEdge::kProperty, "<symbol>");
2185 5 : CHECK(hidden_props);
2186 5 : }
2187 :
2188 :
2189 26644 : TEST(AccessorInfo) {
2190 5 : LocalContext env;
2191 10 : v8::HandleScope scope(env->GetIsolate());
2192 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2193 :
2194 : CompileRun("function foo(x) { }\n");
2195 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2196 5 : CHECK(ValidateSnapshot(snapshot));
2197 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2198 5 : const v8::HeapGraphNode* foo = GetProperty(
2199 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "foo");
2200 5 : CHECK(foo);
2201 : const v8::HeapGraphNode* map =
2202 5 : GetProperty(env->GetIsolate(), foo, v8::HeapGraphEdge::kInternal, "map");
2203 5 : CHECK(map);
2204 5 : const v8::HeapGraphNode* descriptors = GetProperty(
2205 5 : env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "descriptors");
2206 5 : CHECK(descriptors);
2207 5 : const v8::HeapGraphNode* length_name = GetProperty(
2208 5 : env->GetIsolate(), descriptors, v8::HeapGraphEdge::kInternal, "0");
2209 5 : CHECK(length_name);
2210 10 : CHECK_EQ(0, strcmp("length", *v8::String::Utf8Value(env->GetIsolate(),
2211 : length_name->GetName())));
2212 5 : const v8::HeapGraphNode* length_accessor = GetProperty(
2213 5 : env->GetIsolate(), descriptors, v8::HeapGraphEdge::kInternal, "2");
2214 5 : CHECK(length_accessor);
2215 10 : CHECK_EQ(0, strcmp("system / AccessorInfo",
2216 : *v8::String::Utf8Value(env->GetIsolate(),
2217 : length_accessor->GetName())));
2218 5 : const v8::HeapGraphNode* name = GetProperty(
2219 5 : env->GetIsolate(), length_accessor, v8::HeapGraphEdge::kInternal, "name");
2220 5 : CHECK(name);
2221 : const v8::HeapGraphNode* getter =
2222 5 : GetProperty(env->GetIsolate(), length_accessor,
2223 5 : v8::HeapGraphEdge::kInternal, "getter");
2224 5 : CHECK(getter);
2225 : const v8::HeapGraphNode* setter =
2226 5 : GetProperty(env->GetIsolate(), length_accessor,
2227 5 : v8::HeapGraphEdge::kInternal, "setter");
2228 5 : CHECK(setter);
2229 5 : }
2230 :
2231 26644 : TEST(JSGeneratorObject) {
2232 5 : v8::Isolate* isolate = CcTest::isolate();
2233 5 : LocalContext env;
2234 10 : v8::HandleScope scope(isolate);
2235 5 : v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2236 :
2237 : CompileRun(
2238 : "function* foo() { yield 1; }\n"
2239 : "g = foo();\n");
2240 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2241 5 : CHECK(ValidateSnapshot(snapshot));
2242 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2243 : const v8::HeapGraphNode* g =
2244 5 : GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "g");
2245 5 : CHECK(g);
2246 5 : const v8::HeapGraphNode* function = GetProperty(
2247 5 : env->GetIsolate(), g, v8::HeapGraphEdge::kInternal, "function");
2248 5 : CHECK(function);
2249 5 : const v8::HeapGraphNode* context = GetProperty(
2250 5 : env->GetIsolate(), g, v8::HeapGraphEdge::kInternal, "context");
2251 5 : CHECK(context);
2252 5 : const v8::HeapGraphNode* receiver = GetProperty(
2253 5 : env->GetIsolate(), g, v8::HeapGraphEdge::kInternal, "receiver");
2254 5 : CHECK(receiver);
2255 : const v8::HeapGraphNode* parameters_and_registers =
2256 5 : GetProperty(env->GetIsolate(), g, v8::HeapGraphEdge::kInternal,
2257 5 : "parameters_and_registers");
2258 5 : CHECK(parameters_and_registers);
2259 5 : }
2260 :
2261 20 : bool HasWeakEdge(const v8::HeapGraphNode* node) {
2262 140 : for (int i = 0; i < node->GetChildrenCount(); ++i) {
2263 65 : const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
2264 65 : if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
2265 : }
2266 : return false;
2267 : }
2268 :
2269 :
2270 10 : bool HasWeakGlobalHandle() {
2271 10 : v8::Isolate* isolate = CcTest::isolate();
2272 10 : v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2273 10 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2274 10 : CHECK(ValidateSnapshot(snapshot));
2275 10 : const v8::HeapGraphNode* gc_roots = GetNode(
2276 10 : snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2277 10 : CHECK(gc_roots);
2278 : const v8::HeapGraphNode* global_handles = GetNode(
2279 10 : gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
2280 10 : CHECK(global_handles);
2281 10 : return HasWeakEdge(global_handles);
2282 : }
2283 :
2284 :
2285 0 : static void PersistentHandleCallback(
2286 : const v8::WeakCallbackInfo<v8::Persistent<v8::Object> >& data) {
2287 : data.GetParameter()->Reset();
2288 0 : }
2289 :
2290 :
2291 26644 : TEST(WeakGlobalHandle) {
2292 5 : LocalContext env;
2293 10 : v8::HandleScope scope(env->GetIsolate());
2294 :
2295 5 : CHECK(!HasWeakGlobalHandle());
2296 :
2297 : v8::Persistent<v8::Object> handle;
2298 :
2299 15 : handle.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
2300 : handle.SetWeak(&handle, PersistentHandleCallback,
2301 : v8::WeakCallbackType::kParameter);
2302 :
2303 5 : CHECK(HasWeakGlobalHandle());
2304 5 : CcTest::CollectAllGarbage();
2305 5 : EmptyMessageQueues(env->GetIsolate());
2306 5 : }
2307 :
2308 :
2309 26644 : TEST(SfiAndJsFunctionWeakRefs) {
2310 5 : LocalContext env;
2311 10 : v8::HandleScope scope(env->GetIsolate());
2312 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2313 :
2314 : CompileRun(
2315 : "fun = (function (x) { return function () { return x + 1; } })(1);");
2316 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2317 5 : CHECK(ValidateSnapshot(snapshot));
2318 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2319 5 : CHECK(global);
2320 5 : const v8::HeapGraphNode* fun = GetProperty(
2321 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "fun");
2322 5 : CHECK(!HasWeakEdge(fun));
2323 5 : const v8::HeapGraphNode* shared = GetProperty(
2324 5 : env->GetIsolate(), fun, v8::HeapGraphEdge::kInternal, "shared");
2325 5 : CHECK(!HasWeakEdge(shared));
2326 5 : }
2327 :
2328 :
2329 26644 : TEST(AllStrongGcRootsHaveNames) {
2330 5 : LocalContext env;
2331 10 : v8::HandleScope scope(env->GetIsolate());
2332 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2333 :
2334 : CompileRun("foo = {};");
2335 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2336 5 : CHECK(ValidateSnapshot(snapshot));
2337 5 : const v8::HeapGraphNode* gc_roots = GetNode(
2338 5 : snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2339 5 : CHECK(gc_roots);
2340 : const v8::HeapGraphNode* strong_roots = GetNode(
2341 5 : gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
2342 5 : CHECK(strong_roots);
2343 555 : for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
2344 275 : const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
2345 275 : CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
2346 825 : v8::String::Utf8Value name(env->GetIsolate(), edge->GetName());
2347 550 : CHECK(isalpha(**name));
2348 : }
2349 5 : }
2350 :
2351 :
2352 26644 : TEST(NoRefsToNonEssentialEntries) {
2353 5 : LocalContext env;
2354 10 : v8::HandleScope scope(env->GetIsolate());
2355 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2356 : CompileRun("global_object = {};\n");
2357 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2358 5 : CHECK(ValidateSnapshot(snapshot));
2359 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2360 5 : const v8::HeapGraphNode* global_object = GetProperty(
2361 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "global_object");
2362 5 : CHECK(global_object);
2363 : const v8::HeapGraphNode* properties =
2364 5 : GetProperty(env->GetIsolate(), global_object,
2365 5 : v8::HeapGraphEdge::kInternal, "properties");
2366 5 : CHECK(!properties);
2367 : const v8::HeapGraphNode* elements =
2368 5 : GetProperty(env->GetIsolate(), global_object,
2369 5 : v8::HeapGraphEdge::kInternal, "elements");
2370 5 : CHECK(!elements);
2371 5 : }
2372 :
2373 :
2374 26644 : TEST(MapHasDescriptorsAndTransitions) {
2375 5 : LocalContext env;
2376 10 : v8::HandleScope scope(env->GetIsolate());
2377 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2378 : CompileRun("obj = { a: 10 };\n");
2379 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2380 5 : CHECK(ValidateSnapshot(snapshot));
2381 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2382 5 : const v8::HeapGraphNode* global_object = GetProperty(
2383 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj");
2384 5 : CHECK(global_object);
2385 :
2386 5 : const v8::HeapGraphNode* map = GetProperty(
2387 5 : env->GetIsolate(), global_object, v8::HeapGraphEdge::kInternal, "map");
2388 5 : CHECK(map);
2389 5 : const v8::HeapGraphNode* own_descriptors = GetProperty(
2390 5 : env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "descriptors");
2391 5 : CHECK(own_descriptors);
2392 5 : const v8::HeapGraphNode* own_transitions = GetProperty(
2393 5 : env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "transitions");
2394 5 : CHECK(!own_transitions);
2395 5 : }
2396 :
2397 :
2398 26644 : TEST(ManyLocalsInSharedContext) {
2399 : // This test gets very slow with slow asserts (18 minutes instead of 1:30,
2400 : // as of November 2018).
2401 : #ifdef ENABLE_SLOW_DCHECKS
2402 : i::FLAG_enable_slow_asserts = false;
2403 : #endif
2404 5 : LocalContext env;
2405 10 : v8::HandleScope scope(env->GetIsolate());
2406 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2407 : int num_objects = 6000;
2408 : CompileRun(
2409 : "var n = 6000;"
2410 : "var result = [];"
2411 : "result.push('(function outer() {');"
2412 : "for (var i = 0; i < n; i++) {"
2413 : " var f = 'function f_' + i + '() { ';"
2414 : " if (i > 0)"
2415 : " f += 'f_' + (i - 1) + '();';"
2416 : " f += ' }';"
2417 : " result.push(f);"
2418 : "}"
2419 : "result.push('return f_' + (n - 1) + ';');"
2420 : "result.push('})()');"
2421 : "var ok = eval(result.join('\\n'));");
2422 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2423 5 : CHECK(ValidateSnapshot(snapshot));
2424 :
2425 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2426 5 : CHECK(global);
2427 5 : const v8::HeapGraphNode* ok_object = GetProperty(
2428 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "ok");
2429 5 : CHECK(ok_object);
2430 5 : const v8::HeapGraphNode* context_object = GetProperty(
2431 5 : env->GetIsolate(), ok_object, v8::HeapGraphEdge::kInternal, "context");
2432 5 : CHECK(context_object);
2433 : // Check the objects are not duplicated in the context.
2434 5 : CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1,
2435 : context_object->GetChildrenCount());
2436 : // Check all the objects have got their names.
2437 : // ... well check just every 15th because otherwise it's too slow in debug.
2438 4005 : for (int i = 0; i < num_objects - 1; i += 15) {
2439 : i::EmbeddedVector<char, 100> var_name;
2440 2000 : i::SNPrintF(var_name, "f_%d", i);
2441 : const v8::HeapGraphNode* f_object =
2442 2000 : GetProperty(env->GetIsolate(), context_object,
2443 2000 : v8::HeapGraphEdge::kContextVariable, var_name.start());
2444 2000 : CHECK(f_object);
2445 : }
2446 5 : }
2447 :
2448 :
2449 26639 : TEST(AllocationSitesAreVisible) {
2450 : if (i::FLAG_lite_mode) return;
2451 :
2452 0 : LocalContext env;
2453 0 : v8::Isolate* isolate = env->GetIsolate();
2454 0 : v8::HandleScope scope(isolate);
2455 0 : v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2456 : CompileRun(
2457 : "fun = function () { var a = [3, 2, 1]; return a; }\n"
2458 : "fun();");
2459 0 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2460 0 : CHECK(ValidateSnapshot(snapshot));
2461 :
2462 0 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2463 0 : CHECK(global);
2464 0 : const v8::HeapGraphNode* fun_code = GetProperty(
2465 0 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "fun");
2466 0 : CHECK(fun_code);
2467 : const v8::HeapGraphNode* feedback_cell =
2468 0 : GetProperty(env->GetIsolate(), fun_code, v8::HeapGraphEdge::kInternal,
2469 0 : "feedback_cell");
2470 0 : CHECK(feedback_cell);
2471 0 : const v8::HeapGraphNode* vector = GetProperty(
2472 0 : env->GetIsolate(), feedback_cell, v8::HeapGraphEdge::kInternal, "value");
2473 0 : CHECK_EQ(v8::HeapGraphNode::kArray, vector->GetType());
2474 0 : CHECK_EQ(3, vector->GetChildrenCount());
2475 :
2476 : // The first value in the feedback vector should be the boilerplate,
2477 : // after an AllocationSite.
2478 0 : const v8::HeapGraphEdge* prop = vector->GetChild(2);
2479 0 : const v8::HeapGraphNode* allocation_site = prop->GetToNode();
2480 0 : v8::String::Utf8Value name(env->GetIsolate(), allocation_site->GetName());
2481 0 : CHECK_EQ(0, strcmp("system / AllocationSite", *name));
2482 : const v8::HeapGraphNode* transition_info =
2483 0 : GetProperty(env->GetIsolate(), allocation_site,
2484 0 : v8::HeapGraphEdge::kInternal, "transition_info");
2485 0 : CHECK(transition_info);
2486 :
2487 : const v8::HeapGraphNode* elements =
2488 0 : GetProperty(env->GetIsolate(), transition_info,
2489 0 : v8::HeapGraphEdge::kInternal, "elements");
2490 0 : CHECK(elements);
2491 0 : CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType());
2492 0 : CHECK_EQ(v8::internal::FixedArray::SizeFor(3),
2493 : static_cast<int>(elements->GetShallowSize()));
2494 :
2495 : v8::Local<v8::Value> array_val =
2496 0 : heap_profiler->FindObjectById(transition_info->GetId());
2497 0 : CHECK(array_val->IsArray());
2498 : v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(array_val);
2499 : // Verify the array is "a" in the code above.
2500 0 : CHECK_EQ(3u, array->Length());
2501 0 : CHECK(v8::Integer::New(isolate, 3)
2502 : ->Equals(env.local(),
2503 : array->Get(env.local(), v8::Integer::New(isolate, 0))
2504 : .ToLocalChecked())
2505 : .FromJust());
2506 0 : CHECK(v8::Integer::New(isolate, 2)
2507 : ->Equals(env.local(),
2508 : array->Get(env.local(), v8::Integer::New(isolate, 1))
2509 : .ToLocalChecked())
2510 : .FromJust());
2511 0 : CHECK(v8::Integer::New(isolate, 1)
2512 : ->Equals(env.local(),
2513 : array->Get(env.local(), v8::Integer::New(isolate, 2))
2514 : .ToLocalChecked())
2515 : .FromJust());
2516 : }
2517 :
2518 :
2519 26644 : TEST(JSFunctionHasCodeLink) {
2520 5 : LocalContext env;
2521 10 : v8::HandleScope scope(env->GetIsolate());
2522 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2523 : CompileRun("function foo(x, y) { return x + y; }\n");
2524 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2525 5 : CHECK(ValidateSnapshot(snapshot));
2526 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2527 5 : const v8::HeapGraphNode* foo_func = GetProperty(
2528 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "foo");
2529 5 : CHECK(foo_func);
2530 5 : const v8::HeapGraphNode* code = GetProperty(
2531 5 : env->GetIsolate(), foo_func, v8::HeapGraphEdge::kInternal, "code");
2532 5 : CHECK(code);
2533 5 : }
2534 :
2535 10 : static const v8::HeapGraphNode* GetNodeByPath(v8::Isolate* isolate,
2536 : const v8::HeapSnapshot* snapshot,
2537 : const char* path[], int depth) {
2538 10 : const v8::HeapGraphNode* node = snapshot->GetRoot();
2539 70 : for (int current_depth = 0; current_depth < depth; ++current_depth) {
2540 30 : int i, count = node->GetChildrenCount();
2541 2100 : for (i = 0; i < count; ++i) {
2542 1065 : const v8::HeapGraphEdge* edge = node->GetChild(i);
2543 1065 : const v8::HeapGraphNode* to_node = edge->GetToNode();
2544 2100 : v8::String::Utf8Value edge_name(isolate, edge->GetName());
2545 3165 : v8::String::Utf8Value node_name(isolate, to_node->GetName());
2546 : i::EmbeddedVector<char, 100> name;
2547 1065 : i::SNPrintF(name, "%s::%s", *edge_name, *node_name);
2548 2130 : if (strstr(name.start(), path[current_depth])) {
2549 : node = to_node;
2550 30 : break;
2551 : }
2552 : }
2553 30 : if (i == count) return nullptr;
2554 : }
2555 : return node;
2556 : }
2557 :
2558 :
2559 26644 : TEST(CheckCodeNames) {
2560 5 : LocalContext env;
2561 10 : v8::HandleScope scope(env->GetIsolate());
2562 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2563 : CompileRun("var a = 1.1;");
2564 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2565 5 : CHECK(ValidateSnapshot(snapshot));
2566 :
2567 : const char* builtin_path1[] = {"::(GC roots)", "::(Builtins)",
2568 5 : "::(KeyedLoadIC_Slow builtin)"};
2569 5 : const v8::HeapGraphNode* node = GetNodeByPath(
2570 5 : env->GetIsolate(), snapshot, builtin_path1, arraysize(builtin_path1));
2571 5 : CHECK(node);
2572 :
2573 : const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)",
2574 5 : "::(CompileLazy builtin)"};
2575 5 : node = GetNodeByPath(env->GetIsolate(), snapshot, builtin_path2,
2576 5 : arraysize(builtin_path2));
2577 5 : CHECK(node);
2578 15 : v8::String::Utf8Value node_name(env->GetIsolate(), node->GetName());
2579 5 : CHECK_EQ(0, strcmp("(CompileLazy builtin)", *node_name));
2580 5 : }
2581 :
2582 :
2583 : static const char* record_trace_tree_source =
2584 : "var topFunctions = [];\n"
2585 : "var global = this;\n"
2586 : "function generateFunctions(width, depth) {\n"
2587 : " var script = [];\n"
2588 : " for (var i = 0; i < width; i++) {\n"
2589 : " for (var j = 0; j < depth; j++) {\n"
2590 : " script.push('function f_' + i + '_' + j + '(x) {\\n');\n"
2591 : " script.push(' try {\\n');\n"
2592 : " if (j < depth-2) {\n"
2593 : " script.push(' return f_' + i + '_' + (j+1) + '(x+1);\\n');\n"
2594 : " } else if (j == depth - 2) {\n"
2595 : " script.push(' return new f_' + i + '_' + (depth - 1) + '();\\n');\n"
2596 : " } else if (j == depth - 1) {\n"
2597 : " script.push(' this.ts = Date.now();\\n');\n"
2598 : " }\n"
2599 : " script.push(' } catch (e) {}\\n');\n"
2600 : " script.push('}\\n');\n"
2601 : " \n"
2602 : " }\n"
2603 : " }\n"
2604 : " var script = script.join('');\n"
2605 : " // throw script;\n"
2606 : " global.eval(script);\n"
2607 : " for (var i = 0; i < width; i++) {\n"
2608 : " topFunctions.push(this['f_' + i + '_0']);\n"
2609 : " }\n"
2610 : "}\n"
2611 : "\n"
2612 : "var width = 3;\n"
2613 : "var depth = 3;\n"
2614 : "generateFunctions(width, depth);\n"
2615 : "var instances = [];\n"
2616 : "function start() {\n"
2617 : " for (var i = 0; i < width; i++) {\n"
2618 : " instances.push(topFunctions[i](0));\n"
2619 : " }\n"
2620 : "}\n"
2621 : "\n"
2622 : "for (var i = 0; i < 100; i++) start();\n";
2623 :
2624 :
2625 30 : static AllocationTraceNode* FindNode(
2626 : AllocationTracker* tracker, const Vector<const char*>& names) {
2627 : AllocationTraceNode* node = tracker->trace_tree()->root();
2628 330 : for (int i = 0; node != nullptr && i < names.length(); i++) {
2629 180 : const char* name = names[i];
2630 : const std::vector<AllocationTraceNode*>& children = node->children();
2631 : node = nullptr;
2632 125 : for (AllocationTraceNode* child : children) {
2633 : unsigned index = child->function_info_index();
2634 : AllocationTracker::FunctionInfo* info =
2635 250 : tracker->function_info_list()[index];
2636 125 : if (info && strcmp(info->name, name) == 0) {
2637 : node = child;
2638 : break;
2639 : }
2640 : }
2641 : }
2642 30 : return node;
2643 : }
2644 :
2645 :
2646 26644 : TEST(ArrayGrowLeftTrim) {
2647 5 : LocalContext env;
2648 10 : v8::HandleScope scope(env->GetIsolate());
2649 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2650 5 : heap_profiler->StartTrackingHeapObjects(true);
2651 :
2652 : CompileRun(
2653 : "var a = [];\n"
2654 : "for (var i = 0; i < 5; ++i)\n"
2655 : " a[i] = i;\n"
2656 : "for (var i = 0; i < 3; ++i)\n"
2657 : " a.shift();\n");
2658 :
2659 5 : const char* names[] = {""};
2660 : AllocationTracker* tracker =
2661 : reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2662 5 : CHECK(tracker);
2663 : // Resolve all function locations.
2664 5 : tracker->PrepareForSerialization();
2665 : // Print for better diagnostics in case of failure.
2666 5 : tracker->trace_tree()->Print(tracker);
2667 :
2668 5 : AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2669 5 : CHECK(node);
2670 5 : CHECK_GE(node->allocation_count(), 2u);
2671 5 : CHECK_GE(node->allocation_size(), 4u * 5u);
2672 5 : heap_profiler->StopTrackingHeapObjects();
2673 5 : }
2674 :
2675 26644 : TEST(TrackHeapAllocationsWithInlining) {
2676 10 : v8::HandleScope scope(v8::Isolate::GetCurrent());
2677 5 : LocalContext env;
2678 :
2679 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2680 5 : heap_profiler->StartTrackingHeapObjects(true);
2681 :
2682 5 : CompileRun(record_trace_tree_source);
2683 :
2684 : AllocationTracker* tracker =
2685 : reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2686 5 : CHECK(tracker);
2687 : // Resolve all function locations.
2688 5 : tracker->PrepareForSerialization();
2689 : // Print for better diagnostics in case of failure.
2690 5 : tracker->trace_tree()->Print(tracker);
2691 :
2692 5 : const char* names[] = {"", "start", "f_0_0"};
2693 5 : AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2694 5 : CHECK(node);
2695 : // In lite mode, there is feedback and feedback metadata.
2696 : unsigned int num_nodes = (i::FLAG_lite_mode) ? 6 : 8;
2697 : // Without forced source position collection, there is no source position
2698 : // table.
2699 5 : if (i::FLAG_enable_lazy_source_positions) num_nodes -= 1;
2700 5 : CHECK_GE(node->allocation_count(), num_nodes);
2701 5 : CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2702 5 : heap_profiler->StopTrackingHeapObjects();
2703 5 : }
2704 :
2705 26644 : TEST(TrackHeapAllocationsWithoutInlining) {
2706 5 : i::FLAG_turbo_inlining = false;
2707 : // Disable inlining
2708 5 : i::FLAG_max_inlined_bytecode_size = 0;
2709 5 : i::FLAG_max_inlined_bytecode_size_small = 0;
2710 10 : v8::HandleScope scope(v8::Isolate::GetCurrent());
2711 5 : LocalContext env;
2712 :
2713 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2714 5 : heap_profiler->StartTrackingHeapObjects(true);
2715 :
2716 5 : CompileRun(record_trace_tree_source);
2717 :
2718 : AllocationTracker* tracker =
2719 : reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2720 5 : CHECK(tracker);
2721 : // Resolve all function locations.
2722 5 : tracker->PrepareForSerialization();
2723 : // Print for better diagnostics in case of failure.
2724 5 : tracker->trace_tree()->Print(tracker);
2725 :
2726 5 : const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
2727 5 : AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2728 5 : CHECK(node);
2729 5 : CHECK_GE(node->allocation_count(), 100u);
2730 5 : CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2731 5 : heap_profiler->StopTrackingHeapObjects();
2732 5 : }
2733 :
2734 :
2735 : static const char* inline_heap_allocation_source =
2736 : "function f_0(x) {\n"
2737 : " return f_1(x+1);\n"
2738 : "}\n"
2739 : "%NeverOptimizeFunction(f_0);\n"
2740 : "function f_1(x) {\n"
2741 : " return new f_2(x+1);\n"
2742 : "}\n"
2743 : "%NeverOptimizeFunction(f_1);\n"
2744 : "function f_2(x) {\n"
2745 : " this.foo = x;\n"
2746 : "}\n"
2747 : "var instances = [];\n"
2748 : "function start() {\n"
2749 : " instances.push(f_0(0));\n"
2750 : "}\n"
2751 : "\n"
2752 : "for (var i = 0; i < 100; i++) start();\n";
2753 :
2754 :
2755 26644 : TEST(TrackBumpPointerAllocations) {
2756 5 : i::FLAG_allow_natives_syntax = true;
2757 10 : v8::HandleScope scope(v8::Isolate::GetCurrent());
2758 5 : LocalContext env;
2759 :
2760 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2761 5 : const char* names[] = {"", "start", "f_0", "f_1"};
2762 : // First check that normally all allocations are recorded.
2763 : {
2764 5 : heap_profiler->StartTrackingHeapObjects(true);
2765 :
2766 5 : CompileRun(inline_heap_allocation_source);
2767 :
2768 : AllocationTracker* tracker =
2769 : reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2770 5 : CHECK(tracker);
2771 : // Resolve all function locations.
2772 5 : tracker->PrepareForSerialization();
2773 : // Print for better diagnostics in case of failure.
2774 5 : tracker->trace_tree()->Print(tracker);
2775 :
2776 5 : AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2777 5 : CHECK(node);
2778 5 : CHECK_GE(node->allocation_count(), 100u);
2779 5 : CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2780 5 : heap_profiler->StopTrackingHeapObjects();
2781 : }
2782 :
2783 : {
2784 5 : heap_profiler->StartTrackingHeapObjects(true);
2785 :
2786 : // Now check that not all allocations are tracked if we manually reenable
2787 : // inline allocations.
2788 5 : CHECK(CcTest::heap()->inline_allocation_disabled());
2789 5 : CcTest::heap()->EnableInlineAllocation();
2790 :
2791 5 : CompileRun(inline_heap_allocation_source);
2792 :
2793 : AllocationTracker* tracker =
2794 : reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2795 5 : CHECK(tracker);
2796 : // Resolve all function locations.
2797 5 : tracker->PrepareForSerialization();
2798 : // Print for better diagnostics in case of failure.
2799 5 : tracker->trace_tree()->Print(tracker);
2800 :
2801 5 : AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2802 5 : CHECK(node);
2803 5 : CHECK_LT(node->allocation_count(), 100u);
2804 :
2805 5 : CcTest::heap()->DisableInlineAllocation();
2806 5 : heap_profiler->StopTrackingHeapObjects();
2807 : }
2808 5 : }
2809 :
2810 :
2811 26644 : TEST(TrackV8ApiAllocation) {
2812 10 : v8::HandleScope scope(v8::Isolate::GetCurrent());
2813 5 : LocalContext env;
2814 :
2815 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2816 5 : const char* names[] = { "(V8 API)" };
2817 5 : heap_profiler->StartTrackingHeapObjects(true);
2818 :
2819 5 : v8::Local<v8::Object> o1 = v8::Object::New(env->GetIsolate());
2820 5 : o1->Clone();
2821 :
2822 : AllocationTracker* tracker =
2823 : reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2824 5 : CHECK(tracker);
2825 : // Resolve all function locations.
2826 5 : tracker->PrepareForSerialization();
2827 : // Print for better diagnostics in case of failure.
2828 5 : tracker->trace_tree()->Print(tracker);
2829 :
2830 5 : AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2831 5 : CHECK(node);
2832 5 : CHECK_GE(node->allocation_count(), 2u);
2833 5 : CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2834 5 : heap_profiler->StopTrackingHeapObjects();
2835 5 : }
2836 :
2837 :
2838 26644 : TEST(ArrayBufferAndArrayBufferView) {
2839 5 : LocalContext env;
2840 10 : v8::HandleScope scope(env->GetIsolate());
2841 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2842 : CompileRun("arr1 = new Uint32Array(100);\n");
2843 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2844 5 : CHECK(ValidateSnapshot(snapshot));
2845 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2846 5 : const v8::HeapGraphNode* arr1_obj = GetProperty(
2847 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "arr1");
2848 5 : CHECK(arr1_obj);
2849 5 : const v8::HeapGraphNode* arr1_buffer = GetProperty(
2850 5 : env->GetIsolate(), arr1_obj, v8::HeapGraphEdge::kInternal, "buffer");
2851 5 : CHECK(arr1_buffer);
2852 : const v8::HeapGraphNode* backing_store =
2853 5 : GetProperty(env->GetIsolate(), arr1_buffer, v8::HeapGraphEdge::kInternal,
2854 5 : "backing_store");
2855 5 : CHECK(backing_store);
2856 5 : CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize()));
2857 5 : }
2858 :
2859 :
2860 5 : static int GetRetainersCount(const v8::HeapSnapshot* snapshot,
2861 : const v8::HeapGraphNode* node) {
2862 : int count = 0;
2863 33799 : for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) {
2864 33794 : const v8::HeapGraphNode* parent = snapshot->GetNode(i);
2865 154706 : for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) {
2866 120912 : if (parent->GetChild(j)->GetToNode() == node) {
2867 10 : ++count;
2868 : }
2869 : }
2870 : }
2871 5 : return count;
2872 : }
2873 :
2874 :
2875 26644 : TEST(ArrayBufferSharedBackingStore) {
2876 5 : LocalContext env;
2877 5 : v8::Isolate* isolate = env->GetIsolate();
2878 10 : v8::HandleScope handle_scope(isolate);
2879 5 : v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2880 :
2881 5 : v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
2882 5 : CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
2883 5 : CHECK(!ab->IsExternal());
2884 5 : v8::ArrayBuffer::Contents ab_contents = ab->Externalize();
2885 5 : CHECK(ab->IsExternal());
2886 :
2887 5 : CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
2888 : void* data = ab_contents.Data();
2889 5 : CHECK_NOT_NULL(data);
2890 : v8::Local<v8::ArrayBuffer> ab2 =
2891 5 : v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength());
2892 5 : CHECK(ab2->IsExternal());
2893 20 : env->Global()->Set(env.local(), v8_str("ab1"), ab).FromJust();
2894 20 : env->Global()->Set(env.local(), v8_str("ab2"), ab2).FromJust();
2895 :
2896 : v8::Local<v8::Value> result = CompileRun("ab2.byteLength");
2897 10 : CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
2898 :
2899 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2900 5 : CHECK(ValidateSnapshot(snapshot));
2901 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2902 5 : const v8::HeapGraphNode* ab1_node = GetProperty(
2903 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "ab1");
2904 5 : CHECK(ab1_node);
2905 : const v8::HeapGraphNode* ab1_data =
2906 5 : GetProperty(env->GetIsolate(), ab1_node, v8::HeapGraphEdge::kInternal,
2907 5 : "backing_store");
2908 5 : CHECK(ab1_data);
2909 5 : const v8::HeapGraphNode* ab2_node = GetProperty(
2910 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "ab2");
2911 5 : CHECK(ab2_node);
2912 : const v8::HeapGraphNode* ab2_data =
2913 5 : GetProperty(env->GetIsolate(), ab2_node, v8::HeapGraphEdge::kInternal,
2914 5 : "backing_store");
2915 5 : CHECK(ab2_data);
2916 5 : CHECK_EQ(ab1_data, ab2_data);
2917 5 : CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data));
2918 5 : free(data);
2919 5 : }
2920 :
2921 :
2922 26644 : TEST(WeakContainers) {
2923 5 : i::FLAG_allow_natives_syntax = true;
2924 3 : LocalContext env;
2925 8 : v8::HandleScope scope(env->GetIsolate());
2926 7 : if (!CcTest::i_isolate()->use_optimizer()) return;
2927 4 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2928 : CompileRun(
2929 : "function foo(a) { return a.x; }\n"
2930 : "obj = {x : 123};\n"
2931 : "foo(obj);\n"
2932 : "foo(obj);\n"
2933 : "%OptimizeFunctionOnNextCall(foo);\n"
2934 : "foo(obj);\n");
2935 4 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2936 4 : CHECK(ValidateSnapshot(snapshot));
2937 4 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2938 4 : const v8::HeapGraphNode* obj = GetProperty(
2939 4 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj");
2940 4 : CHECK(obj);
2941 : const v8::HeapGraphNode* map =
2942 4 : GetProperty(env->GetIsolate(), obj, v8::HeapGraphEdge::kInternal, "map");
2943 4 : CHECK(map);
2944 4 : const v8::HeapGraphNode* dependent_code = GetProperty(
2945 4 : env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "dependent_code");
2946 4 : if (!dependent_code) return;
2947 3 : int count = dependent_code->GetChildrenCount();
2948 3 : CHECK_NE(0, count);
2949 15 : for (int i = 0; i < count; ++i) {
2950 6 : const v8::HeapGraphEdge* prop = dependent_code->GetChild(i);
2951 6 : CHECK(prop->GetType() == v8::HeapGraphEdge::kInternal ||
2952 : prop->GetType() == v8::HeapGraphEdge::kWeak);
2953 : }
2954 : }
2955 :
2956 26644 : TEST(JSPromise) {
2957 5 : LocalContext env;
2958 10 : v8::HandleScope scope(env->GetIsolate());
2959 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2960 : CompileRun(
2961 : "function A() {}\n"
2962 : "function B() {}\n"
2963 : "resolved = Promise.resolve(new A());\n"
2964 : "rejected = Promise.reject(new B());\n"
2965 : "pending = new Promise(() => 0);\n"
2966 : "chained = pending.then(A, B);\n");
2967 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2968 5 : CHECK(ValidateSnapshot(snapshot));
2969 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2970 :
2971 5 : const v8::HeapGraphNode* resolved = GetProperty(
2972 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "resolved");
2973 5 : CHECK(GetProperty(env->GetIsolate(), resolved, v8::HeapGraphEdge::kInternal,
2974 : "reactions_or_result"));
2975 :
2976 5 : const v8::HeapGraphNode* rejected = GetProperty(
2977 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "rejected");
2978 5 : CHECK(GetProperty(env->GetIsolate(), rejected, v8::HeapGraphEdge::kInternal,
2979 : "reactions_or_result"));
2980 :
2981 5 : const v8::HeapGraphNode* pending = GetProperty(
2982 5 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "pending");
2983 5 : CHECK(GetProperty(env->GetIsolate(), pending, v8::HeapGraphEdge::kInternal,
2984 : "reactions_or_result"));
2985 :
2986 5 : const char* objectNames[] = {"resolved", "rejected", "pending", "chained"};
2987 45 : for (auto objectName : objectNames) {
2988 20 : const v8::HeapGraphNode* promise = GetProperty(
2989 20 : env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, objectName);
2990 20 : EnsureNoUninstrumentedInternals(env->GetIsolate(), promise);
2991 : }
2992 5 : }
2993 :
2994 26644 : TEST(HeapSnapshotScriptContext) {
2995 5 : LocalContext env;
2996 10 : v8::HandleScope scope(env->GetIsolate());
2997 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2998 :
2999 : CompileRun("class Foo{}; const foo = new Foo();");
3000 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
3001 5 : CHECK(ValidateSnapshot(snapshot));
3002 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
3003 : const v8::HeapGraphNode* native_context =
3004 5 : GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kInternal,
3005 5 : "native_context");
3006 5 : CHECK(native_context);
3007 : const v8::HeapGraphNode* script_context_table =
3008 5 : GetProperty(env->GetIsolate(), native_context,
3009 5 : v8::HeapGraphEdge::kInternal, "script_context_table");
3010 5 : CHECK(script_context_table);
3011 : bool found_foo = false;
3012 20 : for (int i = 0, count = script_context_table->GetChildrenCount(); i < count;
3013 : ++i) {
3014 : const v8::HeapGraphNode* context =
3015 15 : script_context_table->GetChild(i)->GetToNode();
3016 15 : const v8::HeapGraphNode* foo = GetProperty(
3017 15 : env->GetIsolate(), context, v8::HeapGraphEdge::kContextVariable, "foo");
3018 15 : if (foo) {
3019 : found_foo = true;
3020 : }
3021 : }
3022 5 : CHECK(found_foo);
3023 5 : }
3024 :
3025 150 : class EmbedderNode : public v8::EmbedderGraph::Node {
3026 : public:
3027 : EmbedderNode(const char* name, size_t size,
3028 : v8::EmbedderGraph::Node* wrapper_node = nullptr)
3029 75 : : name_(name), size_(size), wrapper_node_(wrapper_node) {}
3030 :
3031 : // Graph::Node overrides.
3032 75 : const char* Name() override { return name_; }
3033 65 : size_t SizeInBytes() override { return size_; }
3034 200 : Node* WrapperNode() override { return wrapper_node_; }
3035 :
3036 : private:
3037 : const char* name_;
3038 : size_t size_;
3039 : Node* wrapper_node_;
3040 : };
3041 :
3042 10 : class EmbedderRootNode : public EmbedderNode {
3043 : public:
3044 5 : explicit EmbedderRootNode(const char* name) : EmbedderNode(name, 0) {}
3045 : // Graph::Node override.
3046 10 : bool IsRootNode() override { return true; }
3047 : };
3048 :
3049 : // Used to pass the global object to the BuildEmbedderGraph callback.
3050 : // Otherwise, the callback has to iterate the global handles to find the
3051 : // global object.
3052 : v8::Local<v8::Value>* global_object_pointer;
3053 :
3054 5 : void BuildEmbedderGraph(v8::Isolate* v8_isolate, v8::EmbedderGraph* graph,
3055 : void* data) {
3056 : using Node = v8::EmbedderGraph::Node;
3057 5 : Node* global_node = graph->V8Node(*global_object_pointer);
3058 10 : Node* embedder_node_A = graph->AddNode(
3059 10 : std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeA", 10)));
3060 10 : Node* embedder_node_B = graph->AddNode(
3061 10 : std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeB", 20)));
3062 10 : Node* embedder_node_C = graph->AddNode(
3063 10 : std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeC", 30)));
3064 10 : Node* embedder_root = graph->AddNode(
3065 10 : std::unique_ptr<Node>(new EmbedderRootNode("EmbedderRoot")));
3066 5 : graph->AddEdge(global_node, embedder_node_A);
3067 5 : graph->AddEdge(embedder_node_A, embedder_node_B);
3068 5 : graph->AddEdge(embedder_root, embedder_node_C);
3069 5 : graph->AddEdge(embedder_node_C, global_node);
3070 5 : }
3071 :
3072 5 : void CheckEmbedderGraphSnapshot(v8::Isolate* isolate,
3073 : const v8::HeapSnapshot* snapshot) {
3074 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
3075 : const v8::HeapGraphNode* embedder_node_A =
3076 5 : GetChildByName(global, "EmbedderNodeA");
3077 5 : CHECK_EQ(10, GetSize(embedder_node_A));
3078 : const v8::HeapGraphNode* embedder_node_B =
3079 5 : GetChildByName(embedder_node_A, "EmbedderNodeB");
3080 5 : CHECK_EQ(20, GetSize(embedder_node_B));
3081 : const v8::HeapGraphNode* embedder_root =
3082 : GetRootChild(snapshot, "EmbedderRoot");
3083 5 : CHECK(embedder_root);
3084 : const v8::HeapGraphNode* embedder_node_C =
3085 5 : GetChildByName(embedder_root, "EmbedderNodeC");
3086 5 : CHECK_EQ(30, GetSize(embedder_node_C));
3087 : const v8::HeapGraphNode* global_reference =
3088 5 : GetChildByName(embedder_node_C, "Object");
3089 5 : CHECK(global_reference);
3090 5 : }
3091 :
3092 26644 : TEST(EmbedderGraph) {
3093 5 : i::FLAG_heap_profiler_use_embedder_graph = true;
3094 5 : LocalContext env;
3095 10 : v8::HandleScope scope(env->GetIsolate());
3096 5 : i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
3097 : v8::Local<v8::Value> global_object =
3098 : v8::Utils::ToLocal(i::Handle<i::JSObject>(
3099 10 : (isolate->context()->native_context()->global_object()), isolate));
3100 5 : global_object_pointer = &global_object;
3101 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3102 5 : heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraph, nullptr);
3103 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
3104 5 : CHECK(ValidateSnapshot(snapshot));
3105 5 : CheckEmbedderGraphSnapshot(env->GetIsolate(), snapshot);
3106 5 : }
3107 :
3108 5 : void BuildEmbedderGraphWithNamedEdges(v8::Isolate* v8_isolate,
3109 : v8::EmbedderGraph* graph, void* data) {
3110 : using Node = v8::EmbedderGraph::Node;
3111 5 : Node* global_node = graph->V8Node(*global_object_pointer);
3112 10 : Node* embedder_node_A = graph->AddNode(
3113 10 : std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeA", 10)));
3114 10 : Node* embedder_node_B = graph->AddNode(
3115 10 : std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeB", 20)));
3116 10 : Node* embedder_node_C = graph->AddNode(
3117 10 : std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeC", 30)));
3118 5 : graph->AddEdge(global_node, embedder_node_A, "global_to_a");
3119 5 : graph->AddEdge(embedder_node_A, embedder_node_B, "a_to_b");
3120 5 : graph->AddEdge(embedder_node_B, embedder_node_C);
3121 5 : }
3122 :
3123 5 : void CheckEmbedderGraphWithNamedEdges(v8::Isolate* isolate,
3124 : const v8::HeapSnapshot* snapshot) {
3125 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
3126 : const v8::HeapGraphEdge* global_to_a =
3127 5 : GetEdgeByChildName(global, "EmbedderNodeA");
3128 5 : CHECK(global_to_a);
3129 5 : CHECK_EQ(v8::HeapGraphEdge::kInternal, global_to_a->GetType());
3130 10 : CHECK(global_to_a->GetName()->IsString());
3131 5 : CHECK_EQ(0, strcmp("global_to_a", GetName(global_to_a)));
3132 5 : const v8::HeapGraphNode* embedder_node_A = global_to_a->GetToNode();
3133 5 : CHECK_EQ(0, strcmp("EmbedderNodeA", GetName(embedder_node_A)));
3134 5 : CHECK_EQ(10, GetSize(embedder_node_A));
3135 :
3136 : const v8::HeapGraphEdge* a_to_b =
3137 5 : GetEdgeByChildName(embedder_node_A, "EmbedderNodeB");
3138 5 : CHECK(a_to_b);
3139 10 : CHECK(a_to_b->GetName()->IsString());
3140 5 : CHECK_EQ(0, strcmp("a_to_b", GetName(a_to_b)));
3141 5 : CHECK_EQ(v8::HeapGraphEdge::kInternal, a_to_b->GetType());
3142 5 : const v8::HeapGraphNode* embedder_node_B = a_to_b->GetToNode();
3143 5 : CHECK_EQ(0, strcmp("EmbedderNodeB", GetName(embedder_node_B)));
3144 5 : CHECK_EQ(20, GetSize(embedder_node_B));
3145 :
3146 : const v8::HeapGraphEdge* b_to_c =
3147 5 : GetEdgeByChildName(embedder_node_B, "EmbedderNodeC");
3148 5 : CHECK(b_to_c);
3149 10 : CHECK(b_to_c->GetName()->IsNumber());
3150 5 : CHECK_EQ(v8::HeapGraphEdge::kElement, b_to_c->GetType());
3151 5 : const v8::HeapGraphNode* embedder_node_C = b_to_c->GetToNode();
3152 5 : CHECK_EQ(0, strcmp("EmbedderNodeC", GetName(embedder_node_C)));
3153 5 : CHECK_EQ(30, GetSize(embedder_node_C));
3154 5 : }
3155 :
3156 26644 : TEST(EmbedderGraphWithNamedEdges) {
3157 5 : i::FLAG_heap_profiler_use_embedder_graph = true;
3158 5 : LocalContext env;
3159 10 : v8::HandleScope scope(env->GetIsolate());
3160 5 : i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
3161 : v8::Local<v8::Value> global_object =
3162 : v8::Utils::ToLocal(i::Handle<i::JSObject>(
3163 10 : (isolate->context()->native_context()->global_object()), isolate));
3164 5 : global_object_pointer = &global_object;
3165 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3166 : heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithNamedEdges,
3167 5 : nullptr);
3168 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
3169 5 : CHECK(ValidateSnapshot(snapshot));
3170 5 : CheckEmbedderGraphWithNamedEdges(env->GetIsolate(), snapshot);
3171 5 : }
3172 :
3173 : struct GraphBuildingContext {
3174 : int counter = 0;
3175 : };
3176 :
3177 10 : void CheckEmbedderGraphSnapshotWithContext(
3178 : v8::Isolate* isolate, const v8::HeapSnapshot* snapshot,
3179 : const GraphBuildingContext* context) {
3180 10 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
3181 10 : CHECK_GE(context->counter, 1);
3182 10 : CHECK_LE(context->counter, 2);
3183 :
3184 : const v8::HeapGraphNode* embedder_node_A =
3185 10 : GetChildByName(global, "EmbedderNodeA");
3186 10 : CHECK_EQ(10, GetSize(embedder_node_A));
3187 :
3188 : const v8::HeapGraphNode* embedder_node_B =
3189 10 : GetChildByName(global, "EmbedderNodeB");
3190 10 : if (context->counter == 2) {
3191 5 : CHECK_NOT_NULL(embedder_node_B);
3192 5 : CHECK_EQ(20, GetSize(embedder_node_B));
3193 : } else {
3194 5 : CHECK_NULL(embedder_node_B);
3195 : }
3196 10 : }
3197 :
3198 15 : void BuildEmbedderGraphWithContext(v8::Isolate* v8_isolate,
3199 : v8::EmbedderGraph* graph, void* data) {
3200 : using Node = v8::EmbedderGraph::Node;
3201 : GraphBuildingContext* context = static_cast<GraphBuildingContext*>(data);
3202 15 : Node* global_node = graph->V8Node(*global_object_pointer);
3203 :
3204 15 : CHECK_GE(context->counter, 0);
3205 15 : CHECK_LE(context->counter, 1);
3206 15 : switch (context->counter++) {
3207 : case 0: {
3208 20 : Node* embedder_node_A = graph->AddNode(
3209 20 : std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeA", 10)));
3210 10 : graph->AddEdge(global_node, embedder_node_A);
3211 10 : break;
3212 : }
3213 : case 1: {
3214 10 : Node* embedder_node_B = graph->AddNode(
3215 10 : std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeB", 20)));
3216 5 : graph->AddEdge(global_node, embedder_node_B);
3217 5 : break;
3218 : }
3219 : }
3220 15 : }
3221 :
3222 26644 : TEST(EmbedderGraphMultipleCallbacks) {
3223 5 : i::FLAG_heap_profiler_use_embedder_graph = true;
3224 5 : LocalContext env;
3225 10 : v8::HandleScope scope(env->GetIsolate());
3226 5 : i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
3227 : v8::Local<v8::Value> global_object =
3228 : v8::Utils::ToLocal(i::Handle<i::JSObject>(
3229 10 : (isolate->context()->native_context()->global_object()), isolate));
3230 5 : global_object_pointer = &global_object;
3231 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3232 5 : GraphBuildingContext context;
3233 :
3234 : heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
3235 5 : &context);
3236 : heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
3237 5 : &context);
3238 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
3239 5 : CHECK_EQ(context.counter, 2);
3240 5 : CHECK(ValidateSnapshot(snapshot));
3241 5 : CheckEmbedderGraphSnapshotWithContext(env->GetIsolate(), snapshot, &context);
3242 :
3243 : heap_profiler->RemoveBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
3244 5 : &context);
3245 5 : context.counter = 0;
3246 :
3247 5 : snapshot = heap_profiler->TakeHeapSnapshot();
3248 5 : CHECK_EQ(context.counter, 1);
3249 5 : CHECK(ValidateSnapshot(snapshot));
3250 5 : CheckEmbedderGraphSnapshotWithContext(env->GetIsolate(), snapshot, &context);
3251 5 : }
3252 :
3253 26644 : TEST(StrongHandleAnnotation) {
3254 5 : LocalContext env;
3255 10 : v8::HandleScope scope(env->GetIsolate());
3256 : v8::Persistent<v8::Object> handle1, handle2;
3257 15 : handle1.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
3258 15 : handle2.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
3259 : handle1.AnnotateStrongRetainer("my_label");
3260 : handle2.AnnotateStrongRetainer("my_label");
3261 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3262 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
3263 : const v8::HeapGraphNode* gc_roots = GetRootChild(snapshot, "(GC roots)");
3264 5 : CHECK(gc_roots);
3265 : const v8::HeapGraphNode* global_handles =
3266 5 : GetChildByName(gc_roots, "(Global handles)");
3267 5 : CHECK(global_handles);
3268 : int found = 0;
3269 20 : for (int i = 0, count = global_handles->GetChildrenCount(); i < count; ++i) {
3270 15 : const v8::HeapGraphEdge* edge = global_handles->GetChild(i);
3271 30 : v8::String::Utf8Value edge_name(CcTest::isolate(), edge->GetName());
3272 15 : if (EndsWith(*edge_name, "my_label")) ++found;
3273 : }
3274 5 : CHECK_EQ(2, found);
3275 5 : }
3276 :
3277 5 : void BuildEmbedderGraphWithWrapperNode(v8::Isolate* v8_isolate,
3278 : v8::EmbedderGraph* graph, void* data) {
3279 : using Node = v8::EmbedderGraph::Node;
3280 5 : Node* global_node = graph->V8Node(*global_object_pointer);
3281 10 : Node* wrapper_node = graph->AddNode(
3282 10 : std::unique_ptr<Node>(new EmbedderNode("WrapperNode / TAG", 10)));
3283 10 : Node* embedder_node = graph->AddNode(std::unique_ptr<Node>(
3284 10 : new EmbedderNode("EmbedderNode", 10, wrapper_node)));
3285 : Node* other_node =
3286 15 : graph->AddNode(std::unique_ptr<Node>(new EmbedderNode("OtherNode", 20)));
3287 5 : graph->AddEdge(global_node, embedder_node);
3288 5 : graph->AddEdge(wrapper_node, other_node);
3289 :
3290 10 : Node* wrapper_node2 = graph->AddNode(
3291 10 : std::unique_ptr<Node>(new EmbedderNode("WrapperNode2", 10)));
3292 10 : Node* embedder_node2 = graph->AddNode(std::unique_ptr<Node>(
3293 10 : new EmbedderNode("EmbedderNode2", 10, wrapper_node2)));
3294 5 : graph->AddEdge(global_node, embedder_node2);
3295 5 : graph->AddEdge(embedder_node2, wrapper_node2);
3296 5 : graph->AddEdge(wrapper_node2, other_node);
3297 5 : }
3298 :
3299 26644 : TEST(EmbedderGraphWithWrapperNode) {
3300 5 : i::FLAG_heap_profiler_use_embedder_graph = true;
3301 5 : LocalContext env;
3302 10 : v8::HandleScope scope(env->GetIsolate());
3303 5 : i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
3304 : v8::Local<v8::Value> global_object =
3305 : v8::Utils::ToLocal(i::Handle<i::JSObject>(
3306 10 : (isolate->context()->native_context()->global_object()), isolate));
3307 5 : global_object_pointer = &global_object;
3308 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3309 : heap_profiler->AddBuildEmbedderGraphCallback(
3310 5 : BuildEmbedderGraphWithWrapperNode, nullptr);
3311 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
3312 5 : CHECK(ValidateSnapshot(snapshot));
3313 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
3314 : const v8::HeapGraphNode* embedder_node =
3315 5 : GetChildByName(global, "EmbedderNode / TAG");
3316 : const v8::HeapGraphNode* other_node =
3317 5 : GetChildByName(embedder_node, "OtherNode");
3318 5 : CHECK(other_node);
3319 : const v8::HeapGraphNode* wrapper_node =
3320 5 : GetChildByName(embedder_node, "WrapperNode / TAG");
3321 5 : CHECK(!wrapper_node);
3322 :
3323 : const v8::HeapGraphNode* embedder_node2 =
3324 5 : GetChildByName(global, "EmbedderNode2");
3325 5 : other_node = GetChildByName(embedder_node2, "OtherNode");
3326 5 : CHECK(other_node);
3327 : const v8::HeapGraphNode* wrapper_node2 =
3328 5 : GetChildByName(embedder_node, "WrapperNode2");
3329 5 : CHECK(!wrapper_node2);
3330 5 : }
3331 :
3332 10 : class EmbedderNodeWithPrefix : public v8::EmbedderGraph::Node {
3333 : public:
3334 : EmbedderNodeWithPrefix(const char* prefix, const char* name)
3335 5 : : prefix_(prefix), name_(name) {}
3336 :
3337 : // Graph::Node overrides.
3338 5 : const char* Name() override { return name_; }
3339 5 : size_t SizeInBytes() override { return 0; }
3340 5 : const char* NamePrefix() override { return prefix_; }
3341 :
3342 : private:
3343 : const char* prefix_;
3344 : const char* name_;
3345 : };
3346 :
3347 5 : void BuildEmbedderGraphWithPrefix(v8::Isolate* v8_isolate,
3348 : v8::EmbedderGraph* graph, void* data) {
3349 : using Node = v8::EmbedderGraph::Node;
3350 5 : Node* global_node = graph->V8Node(*global_object_pointer);
3351 10 : Node* node = graph->AddNode(
3352 10 : std::unique_ptr<Node>(new EmbedderNodeWithPrefix("Detached", "Node")));
3353 5 : graph->AddEdge(global_node, node);
3354 5 : }
3355 :
3356 26644 : TEST(EmbedderGraphWithPrefix) {
3357 5 : i::FLAG_heap_profiler_use_embedder_graph = true;
3358 5 : LocalContext env;
3359 10 : v8::HandleScope scope(env->GetIsolate());
3360 5 : i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
3361 : v8::Local<v8::Value> global_object =
3362 : v8::Utils::ToLocal(i::Handle<i::JSObject>(
3363 10 : (isolate->context()->native_context()->global_object()), isolate));
3364 5 : global_object_pointer = &global_object;
3365 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3366 : heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithPrefix,
3367 5 : nullptr);
3368 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
3369 5 : CHECK(ValidateSnapshot(snapshot));
3370 5 : const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
3371 5 : const v8::HeapGraphNode* node = GetChildByName(global, "Detached Node");
3372 5 : CHECK(node);
3373 5 : }
3374 :
3375 : static inline i::Address ToAddress(int n) { return static_cast<i::Address>(n); }
3376 :
3377 26644 : TEST(AddressToTraceMap) {
3378 : i::AddressToTraceMap map;
3379 :
3380 5 : CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(150)));
3381 :
3382 : // [0x100, 0x200) -> 1
3383 5 : map.AddRange(ToAddress(0x100), 0x100, 1U);
3384 5 : CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x50)));
3385 5 : CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x100)));
3386 5 : CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x150)));
3387 5 : CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x100 + 0x100)));
3388 5 : CHECK_EQ(1u, map.size());
3389 :
3390 : // [0x100, 0x200) -> 1, [0x200, 0x300) -> 2
3391 5 : map.AddRange(ToAddress(0x200), 0x100, 2U);
3392 5 : CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x2A0)));
3393 5 : CHECK_EQ(2u, map.size());
3394 :
3395 : // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2
3396 5 : map.AddRange(ToAddress(0x180), 0x100, 3U);
3397 5 : CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
3398 5 : CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
3399 5 : CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
3400 5 : CHECK_EQ(3u, map.size());
3401 :
3402 : // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2,
3403 : // [0x400, 0x500) -> 4
3404 5 : map.AddRange(ToAddress(0x400), 0x100, 4U);
3405 5 : CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
3406 5 : CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
3407 5 : CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
3408 5 : CHECK_EQ(4u, map.GetTraceNodeId(ToAddress(0x450)));
3409 5 : CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x500)));
3410 5 : CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x350)));
3411 5 : CHECK_EQ(4u, map.size());
3412 :
3413 : // [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5
3414 5 : map.AddRange(ToAddress(0x200), 0x400, 5U);
3415 5 : CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
3416 5 : CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x400)));
3417 5 : CHECK_EQ(3u, map.size());
3418 :
3419 : // [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5
3420 5 : map.AddRange(ToAddress(0x180), 0x80, 6U);
3421 5 : map.AddRange(ToAddress(0x180), 0x80, 7U);
3422 5 : CHECK_EQ(7u, map.GetTraceNodeId(ToAddress(0x180)));
3423 5 : CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
3424 5 : CHECK_EQ(3u, map.size());
3425 :
3426 5 : map.Clear();
3427 5 : CHECK_EQ(0u, map.size());
3428 5 : CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x400)));
3429 5 : }
3430 :
3431 44 : static const v8::AllocationProfile::Node* FindAllocationProfileNode(
3432 : v8::Isolate* isolate, v8::AllocationProfile& profile,
3433 : const Vector<const char*>& names) {
3434 44 : v8::AllocationProfile::Node* node = profile.GetRootNode();
3435 394 : for (int i = 0; node != nullptr && i < names.length(); ++i) {
3436 204 : const char* name = names[i];
3437 102 : auto children = node->children;
3438 : node = nullptr;
3439 106 : for (v8::AllocationProfile::Node* child : children) {
3440 110 : v8::String::Utf8Value child_name(isolate, child->name);
3441 106 : if (strcmp(*child_name, name) == 0) {
3442 : node = child;
3443 102 : break;
3444 : }
3445 : }
3446 : }
3447 44 : return node;
3448 : }
3449 :
3450 24 : static void CheckNoZeroCountNodes(v8::AllocationProfile::Node* node) {
3451 88 : for (auto alloc : node->allocations) {
3452 64 : CHECK_GT(alloc.count, 0u);
3453 : }
3454 44 : for (auto child : node->children) {
3455 20 : CheckNoZeroCountNodes(child);
3456 : }
3457 24 : }
3458 :
3459 : static int NumberOfAllocations(const v8::AllocationProfile::Node* node) {
3460 : int count = 0;
3461 90 : for (auto allocation : node->allocations) {
3462 70 : count += allocation.count;
3463 : }
3464 : return count;
3465 : }
3466 :
3467 : static const char* simple_sampling_heap_profiler_script =
3468 : "var A = [];\n"
3469 : "function bar(size) { return new Array(size); }\n"
3470 : "var foo = function() {\n"
3471 : " for (var i = 0; i < 1024; ++i) {\n"
3472 : " A[i] = bar(1024);\n"
3473 : " }\n"
3474 : "}\n"
3475 : "foo();";
3476 :
3477 26643 : TEST(SamplingHeapProfiler) {
3478 8 : v8::HandleScope scope(v8::Isolate::GetCurrent());
3479 4 : LocalContext env;
3480 4 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3481 :
3482 : // Turn off always_opt. Inlining can cause stack traces to be shorter than
3483 : // what we expect in this test.
3484 4 : v8::internal::FLAG_always_opt = false;
3485 :
3486 : // Suppress randomness to avoid flakiness in tests.
3487 4 : v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
3488 :
3489 : // Sample should be empty if requested before sampling has started.
3490 : {
3491 4 : v8::AllocationProfile* profile = heap_profiler->GetAllocationProfile();
3492 4 : CHECK_NULL(profile);
3493 : }
3494 :
3495 : {
3496 4 : heap_profiler->StartSamplingHeapProfiler(1024);
3497 4 : CompileRun(simple_sampling_heap_profiler_script);
3498 :
3499 : std::unique_ptr<v8::AllocationProfile> profile(
3500 4 : heap_profiler->GetAllocationProfile());
3501 4 : CHECK(profile);
3502 :
3503 4 : const char* names[] = {"", "foo", "bar"};
3504 4 : auto node_bar = FindAllocationProfileNode(env->GetIsolate(), *profile,
3505 8 : ArrayVector(names));
3506 4 : CHECK(node_bar);
3507 :
3508 4 : heap_profiler->StopSamplingHeapProfiler();
3509 : }
3510 :
3511 : // Samples should get cleared once sampling is stopped.
3512 : {
3513 4 : v8::AllocationProfile* profile = heap_profiler->GetAllocationProfile();
3514 4 : CHECK_NULL(profile);
3515 : }
3516 :
3517 : // A more complicated test cases with deeper call graph and dynamically
3518 : // generated function names.
3519 : {
3520 4 : heap_profiler->StartSamplingHeapProfiler(64);
3521 4 : CompileRun(record_trace_tree_source);
3522 :
3523 : std::unique_ptr<v8::AllocationProfile> profile(
3524 4 : heap_profiler->GetAllocationProfile());
3525 4 : CHECK(profile);
3526 :
3527 4 : const char* names1[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
3528 4 : auto node1 = FindAllocationProfileNode(env->GetIsolate(), *profile,
3529 8 : ArrayVector(names1));
3530 4 : CHECK(node1);
3531 :
3532 4 : const char* names2[] = {"", "generateFunctions"};
3533 4 : auto node2 = FindAllocationProfileNode(env->GetIsolate(), *profile,
3534 8 : ArrayVector(names2));
3535 4 : CHECK(node2);
3536 :
3537 4 : heap_profiler->StopSamplingHeapProfiler();
3538 : }
3539 :
3540 : // A test case with scripts unloaded before profile gathered
3541 : {
3542 4 : heap_profiler->StartSamplingHeapProfiler(64);
3543 : CompileRun(
3544 : "for (var i = 0; i < 1024; i++) {\n"
3545 : " eval(\"new Array(100)\");\n"
3546 : "}\n");
3547 :
3548 4 : CcTest::CollectAllGarbage();
3549 :
3550 : std::unique_ptr<v8::AllocationProfile> profile(
3551 4 : heap_profiler->GetAllocationProfile());
3552 4 : CHECK(profile);
3553 :
3554 4 : CheckNoZeroCountNodes(profile->GetRootNode());
3555 :
3556 4 : heap_profiler->StopSamplingHeapProfiler();
3557 : }
3558 4 : }
3559 :
3560 26644 : TEST(SamplingHeapProfilerRateAgnosticEstimates) {
3561 10 : v8::HandleScope scope(v8::Isolate::GetCurrent());
3562 5 : LocalContext env;
3563 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3564 :
3565 : // Turn off always_opt. Inlining can cause stack traces to be shorter than
3566 : // what we expect in this test.
3567 5 : v8::internal::FLAG_always_opt = false;
3568 :
3569 : // Disable compilation cache to force compilation in both cases
3570 5 : v8::internal::FLAG_compilation_cache = false;
3571 :
3572 : // Suppress randomness to avoid flakiness in tests.
3573 5 : v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
3574 :
3575 : // stress_incremental_marking adds randomness to the test.
3576 5 : v8::internal::FLAG_stress_incremental_marking = false;
3577 :
3578 : // warmup compilation
3579 5 : CompileRun(simple_sampling_heap_profiler_script);
3580 :
3581 5 : int count_1024 = 0;
3582 : {
3583 5 : heap_profiler->StartSamplingHeapProfiler(1024);
3584 5 : CompileRun(simple_sampling_heap_profiler_script);
3585 :
3586 : std::unique_ptr<v8::AllocationProfile> profile(
3587 5 : heap_profiler->GetAllocationProfile());
3588 5 : CHECK(profile);
3589 :
3590 5 : const char* path_to_foo[] = {"", "foo"};
3591 5 : auto node_foo = FindAllocationProfileNode(env->GetIsolate(), *profile,
3592 10 : ArrayVector(path_to_foo));
3593 5 : CHECK(node_foo);
3594 5 : const char* path_to_bar[] = {"", "foo", "bar"};
3595 5 : auto node_bar = FindAllocationProfileNode(env->GetIsolate(), *profile,
3596 10 : ArrayVector(path_to_bar));
3597 5 : CHECK(node_bar);
3598 :
3599 : // Function bar can be inlined in foo.
3600 5 : count_1024 = NumberOfAllocations(node_foo) + NumberOfAllocations(node_bar);
3601 :
3602 5 : heap_profiler->StopSamplingHeapProfiler();
3603 : }
3604 :
3605 : // Sampling at a higher rate should give us similar numbers of objects.
3606 : {
3607 5 : heap_profiler->StartSamplingHeapProfiler(128);
3608 5 : CompileRun(simple_sampling_heap_profiler_script);
3609 :
3610 : std::unique_ptr<v8::AllocationProfile> profile(
3611 5 : heap_profiler->GetAllocationProfile());
3612 5 : CHECK(profile);
3613 :
3614 5 : const char* path_to_foo[] = {"", "foo"};
3615 5 : auto node_foo = FindAllocationProfileNode(env->GetIsolate(), *profile,
3616 10 : ArrayVector(path_to_foo));
3617 5 : CHECK(node_foo);
3618 5 : const char* path_to_bar[] = {"", "foo", "bar"};
3619 5 : auto node_bar = FindAllocationProfileNode(env->GetIsolate(), *profile,
3620 10 : ArrayVector(path_to_bar));
3621 5 : CHECK(node_bar);
3622 :
3623 : // Function bar can be inlined in foo.
3624 : int count_128 =
3625 5 : NumberOfAllocations(node_foo) + NumberOfAllocations(node_bar);
3626 :
3627 : // We should have similar unsampled counts of allocations. Though
3628 : // we will sample different numbers of objects at different rates,
3629 : // the unsampling process should produce similar final estimates
3630 : // at the true number of allocations. However, the process to
3631 : // determine these unsampled counts is probabilisitic so we need to
3632 : // account for error.
3633 5 : double max_count = std::max(count_128, count_1024);
3634 5 : double min_count = std::min(count_128, count_1024);
3635 5 : double percent_difference = (max_count - min_count) / min_count;
3636 5 : CHECK_LT(percent_difference, 0.1);
3637 :
3638 5 : heap_profiler->StopSamplingHeapProfiler();
3639 : }
3640 5 : }
3641 :
3642 26644 : TEST(SamplingHeapProfilerApiAllocation) {
3643 10 : v8::HandleScope scope(v8::Isolate::GetCurrent());
3644 5 : LocalContext env;
3645 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3646 :
3647 : // Suppress randomness to avoid flakiness in tests.
3648 5 : v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
3649 :
3650 5 : heap_profiler->StartSamplingHeapProfiler(256);
3651 :
3652 81925 : for (int i = 0; i < 8 * 1024; ++i) v8::Object::New(env->GetIsolate());
3653 :
3654 : std::unique_ptr<v8::AllocationProfile> profile(
3655 5 : heap_profiler->GetAllocationProfile());
3656 5 : CHECK(profile);
3657 5 : const char* names[] = {"(V8 API)"};
3658 5 : auto node = FindAllocationProfileNode(env->GetIsolate(), *profile,
3659 10 : ArrayVector(names));
3660 5 : CHECK(node);
3661 :
3662 5 : heap_profiler->StopSamplingHeapProfiler();
3663 5 : }
3664 :
3665 26644 : TEST(SamplingHeapProfilerApiSamples) {
3666 10 : v8::HandleScope scope(v8::Isolate::GetCurrent());
3667 5 : LocalContext env;
3668 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3669 :
3670 : // Suppress randomness to avoid flakiness in tests.
3671 5 : v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
3672 :
3673 5 : heap_profiler->StartSamplingHeapProfiler(1024);
3674 :
3675 : size_t count = 8 * 1024;
3676 81925 : for (size_t i = 0; i < count; ++i) v8::Object::New(env->GetIsolate());
3677 :
3678 : std::unique_ptr<v8::AllocationProfile> profile(
3679 5 : heap_profiler->GetAllocationProfile());
3680 5 : CHECK(profile);
3681 :
3682 : std::vector<v8::AllocationProfile::Node*> nodes_to_visit;
3683 : std::unordered_set<uint32_t> node_ids;
3684 10 : nodes_to_visit.push_back(profile->GetRootNode());
3685 25 : while (!nodes_to_visit.empty()) {
3686 10 : v8::AllocationProfile::Node* node = nodes_to_visit.back();
3687 : nodes_to_visit.pop_back();
3688 10 : CHECK_LT(0, node->node_id);
3689 10 : CHECK_EQ(0, node_ids.count(node->node_id));
3690 10 : node_ids.insert(node->node_id);
3691 : nodes_to_visit.insert(nodes_to_visit.end(), node->children.begin(),
3692 10 : node->children.end());
3693 : }
3694 :
3695 : size_t total_size = 0;
3696 : std::unordered_set<uint64_t> samples_set;
3697 5 : for (auto& sample : profile->GetSamples()) {
3698 2155 : total_size += sample.size * sample.count;
3699 2155 : CHECK_EQ(0, samples_set.count(sample.sample_id));
3700 2155 : CHECK_EQ(1, node_ids.count(sample.node_id));
3701 2155 : CHECK_GT(sample.node_id, 0);
3702 2155 : CHECK_GT(sample.sample_id, 0);
3703 2155 : samples_set.insert(sample.sample_id);
3704 : }
3705 : size_t object_size = total_size / count;
3706 5 : CHECK_GE(object_size, sizeof(void*) * 2);
3707 5 : heap_profiler->StopSamplingHeapProfiler();
3708 5 : }
3709 :
3710 26644 : TEST(SamplingHeapProfilerLeftTrimming) {
3711 10 : v8::HandleScope scope(v8::Isolate::GetCurrent());
3712 5 : LocalContext env;
3713 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3714 :
3715 : // Suppress randomness to avoid flakiness in tests.
3716 5 : v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
3717 :
3718 5 : heap_profiler->StartSamplingHeapProfiler(64);
3719 :
3720 : CompileRun(
3721 : "for (var j = 0; j < 500; ++j) {\n"
3722 : " var a = [];\n"
3723 : " for (var i = 0; i < 5; ++i)\n"
3724 : " a[i] = i;\n"
3725 : " for (var i = 0; i < 3; ++i)\n"
3726 : " a.shift();\n"
3727 : "}\n");
3728 :
3729 5 : CcTest::CollectGarbage(v8::internal::NEW_SPACE);
3730 : // Should not crash.
3731 :
3732 5 : heap_profiler->StopSamplingHeapProfiler();
3733 5 : }
3734 :
3735 26644 : TEST(SamplingHeapProfilerPretenuredInlineAllocations) {
3736 5 : i::FLAG_allow_natives_syntax = true;
3737 5 : i::FLAG_expose_gc = true;
3738 :
3739 5 : CcTest::InitializeVM();
3740 8 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
3741 3 : if (i::FLAG_gc_global || i::FLAG_stress_compaction ||
3742 : i::FLAG_stress_incremental_marking) {
3743 : return;
3744 : }
3745 :
3746 4 : v8::HandleScope scope(v8::Isolate::GetCurrent());
3747 2 : LocalContext env;
3748 2 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3749 :
3750 : // Suppress randomness to avoid flakiness in tests.
3751 2 : v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
3752 :
3753 : // Grow new space until maximum capacity reached.
3754 18 : while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
3755 8 : CcTest::heap()->new_space()->Grow();
3756 : }
3757 :
3758 : i::ScopedVector<char> source(1024);
3759 : i::SNPrintF(source,
3760 : "var number_elements = %d;"
3761 : "var elements = new Array(number_elements);"
3762 : "function f() {"
3763 : " for (var i = 0; i < number_elements; i++) {"
3764 : " elements[i] = [{}, {}, {}];"
3765 : " }"
3766 : " return elements[number_elements - 1];"
3767 : "};"
3768 : "f(); gc();"
3769 : "f(); f();"
3770 : "%%OptimizeFunctionOnNextCall(f);"
3771 : "f();"
3772 : "f;",
3773 2 : i::AllocationSite::kPretenureMinimumCreated + 1);
3774 :
3775 : v8::Local<v8::Function> f =
3776 : v8::Local<v8::Function>::Cast(CompileRun(source.start()));
3777 :
3778 : // Make sure the function is producing pre-tenured objects.
3779 6 : auto res = f->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked();
3780 : i::Handle<i::JSObject> o = i::Handle<i::JSObject>::cast(
3781 : v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
3782 4 : CHECK(CcTest::heap()->InOldSpace(o->elements()));
3783 4 : CHECK(CcTest::heap()->InOldSpace(*o));
3784 :
3785 : // Call the function and profile it.
3786 2 : heap_profiler->StartSamplingHeapProfiler(64);
3787 322 : for (int i = 0; i < 80; ++i) {
3788 480 : f->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked();
3789 : }
3790 :
3791 : std::unique_ptr<v8::AllocationProfile> profile(
3792 2 : heap_profiler->GetAllocationProfile());
3793 2 : CHECK(profile);
3794 2 : heap_profiler->StopSamplingHeapProfiler();
3795 :
3796 2 : const char* names[] = {"f"};
3797 2 : auto node_f = FindAllocationProfileNode(env->GetIsolate(), *profile,
3798 4 : ArrayVector(names));
3799 2 : CHECK(node_f);
3800 :
3801 : int count = 0;
3802 4 : for (auto allocation : node_f->allocations) {
3803 2 : count += allocation.count;
3804 : }
3805 :
3806 2 : CHECK_GE(count, 8000);
3807 : }
3808 :
3809 26644 : TEST(SamplingHeapProfilerLargeInterval) {
3810 10 : v8::HandleScope scope(v8::Isolate::GetCurrent());
3811 5 : LocalContext env;
3812 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3813 :
3814 : // Suppress randomness to avoid flakiness in tests.
3815 5 : v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
3816 :
3817 5 : heap_profiler->StartSamplingHeapProfiler(512 * 1024);
3818 :
3819 81925 : for (int i = 0; i < 8 * 1024; ++i) {
3820 40960 : CcTest::i_isolate()->factory()->NewFixedArray(1024);
3821 : }
3822 :
3823 : std::unique_ptr<v8::AllocationProfile> profile(
3824 5 : heap_profiler->GetAllocationProfile());
3825 5 : CHECK(profile);
3826 5 : const char* names[] = {"(EXTERNAL)"};
3827 5 : auto node = FindAllocationProfileNode(env->GetIsolate(), *profile,
3828 10 : ArrayVector(names));
3829 5 : CHECK(node);
3830 :
3831 5 : heap_profiler->StopSamplingHeapProfiler();
3832 5 : }
3833 :
3834 26644 : TEST(HeapSnapshotPrototypeNotJSReceiver) {
3835 5 : LocalContext env;
3836 10 : v8::HandleScope scope(env->GetIsolate());
3837 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3838 : CompileRun(
3839 : "function object() {}"
3840 : "object.prototype = 42;");
3841 5 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
3842 5 : CHECK(ValidateSnapshot(snapshot));
3843 5 : }
3844 :
3845 26644 : TEST(SamplingHeapProfilerSampleDuringDeopt) {
3846 5 : i::FLAG_allow_natives_syntax = true;
3847 :
3848 10 : v8::HandleScope scope(v8::Isolate::GetCurrent());
3849 5 : LocalContext env;
3850 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3851 :
3852 : // Suppress randomness to avoid flakiness in tests.
3853 5 : v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
3854 :
3855 : // Small sample interval to force each object to be sampled.
3856 5 : heap_profiler->StartSamplingHeapProfiler(i::kTaggedSize);
3857 :
3858 : // Lazy deopt from runtime call from inlined callback function.
3859 : const char* source =
3860 : "var b = "
3861 : " [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];"
3862 : "(function f() {"
3863 : " var result = 0;"
3864 : " var lazyDeopt = function(deopt) {"
3865 : " var callback = function(v,i,o) {"
3866 : " result += i;"
3867 : " if (i == 13 && deopt) {"
3868 : " %DeoptimizeNow();"
3869 : " }"
3870 : " return v;"
3871 : " };"
3872 : " b.map(callback);"
3873 : " };"
3874 : " lazyDeopt();"
3875 : " lazyDeopt();"
3876 : " %OptimizeFunctionOnNextCall(lazyDeopt);"
3877 : " lazyDeopt();"
3878 : " lazyDeopt(true);"
3879 : " lazyDeopt();"
3880 : "})();";
3881 :
3882 : CompileRun(source);
3883 : // Should not crash.
3884 :
3885 : std::unique_ptr<v8::AllocationProfile> profile(
3886 5 : heap_profiler->GetAllocationProfile());
3887 5 : CHECK(profile);
3888 5 : heap_profiler->StopSamplingHeapProfiler();
3889 5 : }
3890 :
3891 26643 : TEST(WeakReference) {
3892 4 : v8::Isolate* isolate = CcTest::isolate();
3893 : i::Isolate* i_isolate = CcTest::i_isolate();
3894 : i::Factory* factory = i_isolate->factory();
3895 : i::HandleScope scope(i_isolate);
3896 4 : LocalContext env;
3897 :
3898 : // Create a FeedbackVector.
3899 : v8::Local<v8::Script> script =
3900 4 : v8::Script::Compile(isolate->GetCurrentContext(),
3901 4 : v8::String::NewFromUtf8(isolate, "function foo() {}",
3902 : v8::NewStringType::kNormal)
3903 4 : .ToLocalChecked())
3904 : .ToLocalChecked();
3905 4 : v8::MaybeLocal<v8::Value> value = script->Run(isolate->GetCurrentContext());
3906 4 : CHECK(!value.IsEmpty());
3907 :
3908 : i::Handle<i::Object> obj = v8::Utils::OpenHandle(*script);
3909 : i::Handle<i::SharedFunctionInfo> shared_function =
3910 : i::Handle<i::SharedFunctionInfo>(i::JSFunction::cast(*obj)->shared(),
3911 : i_isolate);
3912 : i::Handle<i::ClosureFeedbackCellArray> feedback_cell_array =
3913 4 : i::ClosureFeedbackCellArray::New(i_isolate, shared_function);
3914 : i::Handle<i::FeedbackVector> fv =
3915 4 : factory->NewFeedbackVector(shared_function, feedback_cell_array);
3916 :
3917 : // Create a Code.
3918 16 : i::Assembler assm(i::AssemblerOptions{});
3919 4 : assm.nop(); // supported on all architectures
3920 4 : i::CodeDesc desc;
3921 : assm.GetCode(i_isolate, &desc);
3922 : i::Handle<i::Code> code =
3923 8 : factory->NewCode(desc, i::Code::STUB, i::Handle<i::Code>());
3924 4 : CHECK(code->IsCode());
3925 :
3926 8 : fv->set_optimized_code_weak_or_smi(i::HeapObjectReference::Weak(*code));
3927 :
3928 4 : v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
3929 4 : const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
3930 4 : CHECK(ValidateSnapshot(snapshot));
3931 4 : }
3932 :
3933 26644 : TEST(Bug8373_1) {
3934 5 : LocalContext env;
3935 10 : v8::HandleScope scope(env->GetIsolate());
3936 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3937 :
3938 5 : heap_profiler->StartSamplingHeapProfiler(100);
3939 :
3940 5 : heap_profiler->TakeHeapSnapshot();
3941 : // Causes the StringsStorage to be deleted.
3942 5 : heap_profiler->DeleteAllHeapSnapshots();
3943 :
3944 : // Triggers an allocation sample that tries to use the StringsStorage.
3945 20485 : for (int i = 0; i < 2 * 1024; ++i) {
3946 : CompileRun(
3947 : "new Array(64);"
3948 : "new Uint8Array(16);");
3949 : }
3950 :
3951 5 : heap_profiler->StopSamplingHeapProfiler();
3952 5 : }
3953 :
3954 26644 : TEST(Bug8373_2) {
3955 5 : LocalContext env;
3956 10 : v8::HandleScope scope(env->GetIsolate());
3957 5 : v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
3958 :
3959 5 : heap_profiler->StartTrackingHeapObjects(true);
3960 :
3961 5 : heap_profiler->TakeHeapSnapshot();
3962 : // Causes the StringsStorage to be deleted.
3963 5 : heap_profiler->DeleteAllHeapSnapshots();
3964 :
3965 : // Triggers an allocations that try to use the StringsStorage.
3966 20485 : for (int i = 0; i < 2 * 1024; ++i) {
3967 : CompileRun(
3968 : "new Array(64);"
3969 : "new Uint8Array(16);");
3970 : }
3971 :
3972 5 : heap_profiler->StopTrackingHeapObjects();
3973 79922 : }
|