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