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 "src/v8.h"
33
34 #include "include/v8-profiler.h"
35 #include "src/debug/debug.h"
36 #include "src/hashmap.h"
37 #include "src/profiler/allocation-tracker.h"
38 #include "src/profiler/heap-profiler.h"
39 #include "src/profiler/heap-snapshot-generator-inl.h"
40 #include "test/cctest/cctest.h"
41
42 using i::AllocationTraceNode;
43 using i::AllocationTraceTree;
44 using i::AllocationTracker;
45 using i::HashMap;
46 using i::Vector;
47
48 namespace {
49
50 class NamedEntriesDetector {
51 public:
NamedEntriesDetector()52 NamedEntriesDetector()
53 : has_A2(false), has_B2(false), has_C2(false) {
54 }
55
CheckEntry(i::HeapEntry * entry)56 void CheckEntry(i::HeapEntry* entry) {
57 if (strcmp(entry->name(), "A2") == 0) has_A2 = true;
58 if (strcmp(entry->name(), "B2") == 0) has_B2 = true;
59 if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
60 }
61
AddressesMatch(void * key1,void * key2)62 static bool AddressesMatch(void* key1, void* key2) {
63 return key1 == key2;
64 }
65
CheckAllReachables(i::HeapEntry * root)66 void CheckAllReachables(i::HeapEntry* root) {
67 i::HashMap visited(AddressesMatch);
68 i::List<i::HeapEntry*> list(10);
69 list.Add(root);
70 CheckEntry(root);
71 while (!list.is_empty()) {
72 i::HeapEntry* entry = list.RemoveLast();
73 i::Vector<i::HeapGraphEdge*> children = entry->children();
74 for (int i = 0; i < children.length(); ++i) {
75 if (children[i]->type() == i::HeapGraphEdge::kShortcut) continue;
76 i::HeapEntry* child = children[i]->to();
77 i::HashMap::Entry* entry = visited.LookupOrInsert(
78 reinterpret_cast<void*>(child),
79 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)));
80 if (entry->value)
81 continue;
82 entry->value = reinterpret_cast<void*>(1);
83 list.Add(child);
84 CheckEntry(child);
85 }
86 }
87 }
88
89 bool has_A2;
90 bool has_B2;
91 bool has_C2;
92 };
93
94 } // namespace
95
96
GetGlobalObject(const v8::HeapSnapshot * snapshot)97 static const v8::HeapGraphNode* GetGlobalObject(
98 const v8::HeapSnapshot* snapshot) {
99 CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount());
100 // The 0th-child is (GC Roots), 1st is the user root.
101 const v8::HeapGraphNode* global_obj =
102 snapshot->GetRoot()->GetChild(1)->GetToNode();
103 CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
104 reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
105 return global_obj;
106 }
107
108
GetProperty(const v8::HeapGraphNode * node,v8::HeapGraphEdge::Type type,const char * name)109 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
110 v8::HeapGraphEdge::Type type,
111 const char* name) {
112 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
113 const v8::HeapGraphEdge* prop = node->GetChild(i);
114 v8::String::Utf8Value prop_name(prop->GetName());
115 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
116 return prop->GetToNode();
117 }
118 return NULL;
119 }
120
121
HasString(const v8::HeapGraphNode * node,const char * contents)122 static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
123 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
124 const v8::HeapGraphEdge* prop = node->GetChild(i);
125 const v8::HeapGraphNode* node = prop->GetToNode();
126 if (node->GetType() == v8::HeapGraphNode::kString) {
127 v8::String::Utf8Value node_name(node->GetName());
128 if (strcmp(contents, *node_name) == 0) return true;
129 }
130 }
131 return false;
132 }
133
134
AddressesMatch(void * key1,void * key2)135 static bool AddressesMatch(void* key1, void* key2) {
136 return key1 == key2;
137 }
138
139
140 // Check that snapshot has no unretained entries except root.
ValidateSnapshot(const v8::HeapSnapshot * snapshot,int depth=3)141 static bool ValidateSnapshot(const v8::HeapSnapshot* snapshot, int depth = 3) {
142 i::HeapSnapshot* heap_snapshot = const_cast<i::HeapSnapshot*>(
143 reinterpret_cast<const i::HeapSnapshot*>(snapshot));
144
145 i::HashMap visited(AddressesMatch);
146 i::List<i::HeapGraphEdge>& edges = heap_snapshot->edges();
147 for (int i = 0; i < edges.length(); ++i) {
148 i::HashMap::Entry* entry = visited.LookupOrInsert(
149 reinterpret_cast<void*>(edges[i].to()),
150 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(edges[i].to())));
151 uint32_t ref_count = static_cast<uint32_t>(
152 reinterpret_cast<uintptr_t>(entry->value));
153 entry->value = reinterpret_cast<void*>(ref_count + 1);
154 }
155 uint32_t unretained_entries_count = 0;
156 i::List<i::HeapEntry>& entries = heap_snapshot->entries();
157 for (int i = 0; i < entries.length(); ++i) {
158 i::HashMap::Entry* entry = visited.Lookup(
159 reinterpret_cast<void*>(&entries[i]),
160 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&entries[i])));
161 if (!entry && entries[i].id() != 1) {
162 entries[i].Print("entry with no retainer", "", depth, 0);
163 ++unretained_entries_count;
164 }
165 }
166 return unretained_entries_count == 0;
167 }
168
169
TEST(HeapSnapshot)170 TEST(HeapSnapshot) {
171 LocalContext env2;
172 v8::HandleScope scope(env2->GetIsolate());
173 v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler();
174
175 CompileRun(
176 "function A2() {}\n"
177 "function B2(x) { return function() { return typeof x; }; }\n"
178 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
179 "var a2 = new A2();\n"
180 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
181 "var c2 = new C2(a2);");
182 const v8::HeapSnapshot* snapshot_env2 = heap_profiler->TakeHeapSnapshot();
183 CHECK(ValidateSnapshot(snapshot_env2));
184 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
185
186 // Verify, that JS global object of env2 has '..2' properties.
187 const v8::HeapGraphNode* a2_node =
188 GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
189 CHECK(a2_node);
190 CHECK(GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
191 CHECK(GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
192 CHECK(GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
193
194 NamedEntriesDetector det;
195 det.CheckAllReachables(const_cast<i::HeapEntry*>(
196 reinterpret_cast<const i::HeapEntry*>(global_env2)));
197 CHECK(det.has_A2);
198 CHECK(det.has_B2);
199 CHECK(det.has_C2);
200 }
201
202
TEST(HeapSnapshotObjectSizes)203 TEST(HeapSnapshotObjectSizes) {
204 LocalContext env;
205 v8::HandleScope scope(env->GetIsolate());
206 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
207
208 // -a-> X1 --a
209 // x -b-> X2 <-|
210 CompileRun(
211 "function X(a, b) { this.a = a; this.b = b; }\n"
212 "x = new X(new X(), new X());\n"
213 "dummy = new X();\n"
214 "(function() { x.a.a = x.b; })();");
215 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
216 CHECK(ValidateSnapshot(snapshot));
217 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
218 const v8::HeapGraphNode* x =
219 GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
220 CHECK(x);
221 const v8::HeapGraphNode* x1 =
222 GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
223 CHECK(x1);
224 const v8::HeapGraphNode* x2 =
225 GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
226 CHECK(x2);
227
228 // Test sizes.
229 CHECK_NE(0, static_cast<int>(x->GetShallowSize()));
230 CHECK_NE(0, static_cast<int>(x1->GetShallowSize()));
231 CHECK_NE(0, static_cast<int>(x2->GetShallowSize()));
232 }
233
234
TEST(BoundFunctionInSnapshot)235 TEST(BoundFunctionInSnapshot) {
236 LocalContext env;
237 v8::HandleScope scope(env->GetIsolate());
238 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
239 CompileRun(
240 "function myFunction(a, b) { this.a = a; this.b = b; }\n"
241 "function AAAAA() {}\n"
242 "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
243 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
244 CHECK(ValidateSnapshot(snapshot));
245 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
246 const v8::HeapGraphNode* f =
247 GetProperty(global, v8::HeapGraphEdge::kProperty, "boundFunction");
248 CHECK(f);
249 CHECK(v8_str("native_bind")->Equals(env.local(), f->GetName()).FromJust());
250 const v8::HeapGraphNode* bindings =
251 GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings");
252 CHECK(bindings);
253 CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
254 CHECK_EQ(1, bindings->GetChildrenCount());
255
256 const v8::HeapGraphNode* bound_this = GetProperty(
257 f, v8::HeapGraphEdge::kShortcut, "bound_this");
258 CHECK(bound_this);
259 CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());
260
261 const v8::HeapGraphNode* bound_function = GetProperty(
262 f, v8::HeapGraphEdge::kShortcut, "bound_function");
263 CHECK(bound_function);
264 CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());
265
266 const v8::HeapGraphNode* bound_argument = GetProperty(
267 f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
268 CHECK(bound_argument);
269 CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
270 }
271
272
TEST(HeapSnapshotEntryChildren)273 TEST(HeapSnapshotEntryChildren) {
274 LocalContext env;
275 v8::HandleScope scope(env->GetIsolate());
276 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
277
278 CompileRun(
279 "function A() { }\n"
280 "a = new A;");
281 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
282 CHECK(ValidateSnapshot(snapshot));
283 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
284 for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
285 const v8::HeapGraphEdge* prop = global->GetChild(i);
286 CHECK_EQ(global, prop->GetFromNode());
287 }
288 const v8::HeapGraphNode* a =
289 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
290 CHECK(a);
291 for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
292 const v8::HeapGraphEdge* prop = a->GetChild(i);
293 CHECK_EQ(a, prop->GetFromNode());
294 }
295 }
296
297
TEST(HeapSnapshotCodeObjects)298 TEST(HeapSnapshotCodeObjects) {
299 LocalContext env;
300 v8::HandleScope scope(env->GetIsolate());
301 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
302
303 CompileRun(
304 "function lazy(x) { return x - 1; }\n"
305 "function compiled(x) { return x + 1; }\n"
306 "var anonymous = (function() { return function() { return 0; } })();\n"
307 "compiled(1)");
308 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
309 CHECK(ValidateSnapshot(snapshot));
310
311 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
312 const v8::HeapGraphNode* compiled =
313 GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
314 CHECK(compiled);
315 CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
316 const v8::HeapGraphNode* lazy =
317 GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
318 CHECK(lazy);
319 CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
320 const v8::HeapGraphNode* anonymous =
321 GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
322 CHECK(anonymous);
323 CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
324 v8::String::Utf8Value anonymous_name(anonymous->GetName());
325 CHECK_EQ(0, strcmp("", *anonymous_name));
326
327 // Find references to code.
328 const v8::HeapGraphNode* compiled_code =
329 GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared");
330 CHECK(compiled_code);
331 const v8::HeapGraphNode* lazy_code =
332 GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
333 CHECK(lazy_code);
334
335 // Check that there's no strong next_code_link. There might be a weak one
336 // but might be not, so we can't check that fact.
337 const v8::HeapGraphNode* code =
338 GetProperty(compiled_code, v8::HeapGraphEdge::kInternal, "code");
339 CHECK(code);
340 const v8::HeapGraphNode* next_code_link =
341 GetProperty(code, v8::HeapGraphEdge::kInternal, "code");
342 CHECK(!next_code_link);
343
344 // Verify that non-compiled code doesn't contain references to "x"
345 // literal, while compiled code does. The scope info is stored in FixedArray
346 // objects attached to the SharedFunctionInfo.
347 bool compiled_references_x = false, lazy_references_x = false;
348 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
349 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
350 const v8::HeapGraphNode* node = prop->GetToNode();
351 if (node->GetType() == v8::HeapGraphNode::kArray) {
352 if (HasString(node, "x")) {
353 compiled_references_x = true;
354 break;
355 }
356 }
357 }
358 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
359 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
360 const v8::HeapGraphNode* node = prop->GetToNode();
361 if (node->GetType() == v8::HeapGraphNode::kArray) {
362 if (HasString(node, "x")) {
363 lazy_references_x = true;
364 break;
365 }
366 }
367 }
368 CHECK(compiled_references_x);
369 CHECK(!lazy_references_x);
370 }
371
372
TEST(HeapSnapshotHeapNumbers)373 TEST(HeapSnapshotHeapNumbers) {
374 LocalContext env;
375 v8::HandleScope scope(env->GetIsolate());
376 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
377 CompileRun(
378 "a = 1; // a is Smi\n"
379 "b = 2.5; // b is HeapNumber");
380 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
381 CHECK(ValidateSnapshot(snapshot));
382 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
383 CHECK(!GetProperty(global, v8::HeapGraphEdge::kProperty, "a"));
384 const v8::HeapGraphNode* b =
385 GetProperty(global, v8::HeapGraphEdge::kProperty, "b");
386 CHECK(b);
387 CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
388 }
389
390
TEST(HeapSnapshotSlicedString)391 TEST(HeapSnapshotSlicedString) {
392 LocalContext env;
393 v8::HandleScope scope(env->GetIsolate());
394 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
395 CompileRun(
396 "parent_string = \"123456789.123456789.123456789.123456789.123456789."
397 "123456789.123456789.123456789.123456789.123456789."
398 "123456789.123456789.123456789.123456789.123456789."
399 "123456789.123456789.123456789.123456789.123456789."
400 "123456789.123456789.123456789.123456789.123456789."
401 "123456789.123456789.123456789.123456789.123456789."
402 "123456789.123456789.123456789.123456789.123456789."
403 "123456789.123456789.123456789.123456789.123456789.\";"
404 "child_string = parent_string.slice(100);");
405 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
406 CHECK(ValidateSnapshot(snapshot));
407 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
408 const v8::HeapGraphNode* parent_string =
409 GetProperty(global, v8::HeapGraphEdge::kProperty, "parent_string");
410 CHECK(parent_string);
411 const v8::HeapGraphNode* child_string =
412 GetProperty(global, v8::HeapGraphEdge::kProperty, "child_string");
413 CHECK(child_string);
414 CHECK_EQ(v8::HeapGraphNode::kSlicedString, child_string->GetType());
415 const v8::HeapGraphNode* parent =
416 GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent");
417 CHECK_EQ(parent_string, parent);
418 heap_profiler->DeleteAllHeapSnapshots();
419 }
420
421
TEST(HeapSnapshotConsString)422 TEST(HeapSnapshotConsString) {
423 v8::Isolate* isolate = CcTest::isolate();
424 v8::HandleScope scope(isolate);
425 v8::Local<v8::ObjectTemplate> global_template =
426 v8::ObjectTemplate::New(isolate);
427 global_template->SetInternalFieldCount(1);
428 LocalContext env(NULL, global_template);
429 v8::Local<v8::Object> global_proxy = env->Global();
430 v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
431 CHECK_EQ(1, global->InternalFieldCount());
432
433 i::Factory* factory = CcTest::i_isolate()->factory();
434 i::Handle<i::String> first = factory->NewStringFromStaticChars("0123456789");
435 i::Handle<i::String> second = factory->NewStringFromStaticChars("0123456789");
436 i::Handle<i::String> cons_string =
437 factory->NewConsString(first, second).ToHandleChecked();
438
439 global->SetInternalField(0, v8::ToApiHandle<v8::String>(cons_string));
440
441 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
442 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
443 CHECK(ValidateSnapshot(snapshot));
444 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
445
446 const v8::HeapGraphNode* string_node =
447 GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0");
448 CHECK(string_node);
449 CHECK_EQ(v8::HeapGraphNode::kConsString, string_node->GetType());
450
451 const v8::HeapGraphNode* first_node =
452 GetProperty(string_node, v8::HeapGraphEdge::kInternal, "first");
453 CHECK_EQ(v8::HeapGraphNode::kString, first_node->GetType());
454
455 const v8::HeapGraphNode* second_node =
456 GetProperty(string_node, v8::HeapGraphEdge::kInternal, "second");
457 CHECK_EQ(v8::HeapGraphNode::kString, second_node->GetType());
458
459 heap_profiler->DeleteAllHeapSnapshots();
460 }
461
462
TEST(HeapSnapshotSymbol)463 TEST(HeapSnapshotSymbol) {
464 LocalContext env;
465 v8::HandleScope scope(env->GetIsolate());
466 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
467
468 CompileRun("a = Symbol('mySymbol');\n");
469 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
470 CHECK(ValidateSnapshot(snapshot));
471 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
472 const v8::HeapGraphNode* a =
473 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
474 CHECK(a);
475 CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol);
476 CHECK(v8_str("symbol")->Equals(env.local(), a->GetName()).FromJust());
477 const v8::HeapGraphNode* name =
478 GetProperty(a, v8::HeapGraphEdge::kInternal, "name");
479 CHECK(name);
480 CHECK(v8_str("mySymbol")->Equals(env.local(), name->GetName()).FromJust());
481 }
482
483
CheckSimdSnapshot(const char * program,const char * var_name)484 void CheckSimdSnapshot(const char* program, const char* var_name) {
485 i::FLAG_harmony_simd = true;
486 LocalContext env;
487 v8::HandleScope scope(env->GetIsolate());
488 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
489
490 CompileRun(program);
491 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
492 CHECK(ValidateSnapshot(snapshot));
493 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
494 const v8::HeapGraphNode* var =
495 GetProperty(global, v8::HeapGraphEdge::kProperty, var_name);
496 CHECK(var);
497 CHECK_EQ(var->GetType(), v8::HeapGraphNode::kSimdValue);
498 }
499
500
TEST(HeapSnapshotSimd)501 TEST(HeapSnapshotSimd) {
502 CheckSimdSnapshot("a = SIMD.Float32x4();\n", "a");
503 CheckSimdSnapshot("a = SIMD.Int32x4();\n", "a");
504 CheckSimdSnapshot("a = SIMD.Uint32x4();\n", "a");
505 CheckSimdSnapshot("a = SIMD.Bool32x4();\n", "a");
506 CheckSimdSnapshot("a = SIMD.Int16x8();\n", "a");
507 CheckSimdSnapshot("a = SIMD.Uint16x8();\n", "a");
508 CheckSimdSnapshot("a = SIMD.Bool16x8();\n", "a");
509 CheckSimdSnapshot("a = SIMD.Int8x16();\n", "a");
510 CheckSimdSnapshot("a = SIMD.Uint8x16();\n", "a");
511 CheckSimdSnapshot("a = SIMD.Bool8x16();\n", "a");
512 }
513
514
TEST(HeapSnapshotWeakCollection)515 TEST(HeapSnapshotWeakCollection) {
516 LocalContext env;
517 v8::HandleScope scope(env->GetIsolate());
518 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
519
520 CompileRun(
521 "k = {}; v = {}; s = 'str';\n"
522 "ws = new WeakSet(); ws.add(k); ws.add(v); ws[s] = s;\n"
523 "wm = new WeakMap(); wm.set(k, v); wm[s] = s;\n");
524 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
525 CHECK(ValidateSnapshot(snapshot));
526 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
527 const v8::HeapGraphNode* k =
528 GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
529 CHECK(k);
530 const v8::HeapGraphNode* v =
531 GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
532 CHECK(v);
533 const v8::HeapGraphNode* s =
534 GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
535 CHECK(s);
536
537 const v8::HeapGraphNode* ws =
538 GetProperty(global, v8::HeapGraphEdge::kProperty, "ws");
539 CHECK(ws);
540 CHECK_EQ(v8::HeapGraphNode::kObject, ws->GetType());
541 CHECK(v8_str("WeakSet")->Equals(env.local(), ws->GetName()).FromJust());
542
543 const v8::HeapGraphNode* ws_table =
544 GetProperty(ws, v8::HeapGraphEdge::kInternal, "table");
545 CHECK_EQ(v8::HeapGraphNode::kArray, ws_table->GetType());
546 CHECK_GT(ws_table->GetChildrenCount(), 0);
547 int weak_entries = 0;
548 for (int i = 0, count = ws_table->GetChildrenCount(); i < count; ++i) {
549 const v8::HeapGraphEdge* prop = ws_table->GetChild(i);
550 if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
551 if (k->GetId() == prop->GetToNode()->GetId()) {
552 ++weak_entries;
553 }
554 }
555 CHECK_EQ(1, weak_entries);
556 const v8::HeapGraphNode* ws_s =
557 GetProperty(ws, v8::HeapGraphEdge::kProperty, "str");
558 CHECK(ws_s);
559 CHECK_EQ(s->GetId(), ws_s->GetId());
560
561 const v8::HeapGraphNode* wm =
562 GetProperty(global, v8::HeapGraphEdge::kProperty, "wm");
563 CHECK(wm);
564 CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType());
565 CHECK(v8_str("WeakMap")->Equals(env.local(), wm->GetName()).FromJust());
566
567 const v8::HeapGraphNode* wm_table =
568 GetProperty(wm, v8::HeapGraphEdge::kInternal, "table");
569 CHECK_EQ(v8::HeapGraphNode::kArray, wm_table->GetType());
570 CHECK_GT(wm_table->GetChildrenCount(), 0);
571 weak_entries = 0;
572 for (int i = 0, count = wm_table->GetChildrenCount(); i < count; ++i) {
573 const v8::HeapGraphEdge* prop = wm_table->GetChild(i);
574 if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
575 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
576 if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
577 ++weak_entries;
578 }
579 }
580 CHECK_EQ(2, weak_entries);
581 const v8::HeapGraphNode* wm_s =
582 GetProperty(wm, v8::HeapGraphEdge::kProperty, "str");
583 CHECK(wm_s);
584 CHECK_EQ(s->GetId(), wm_s->GetId());
585 }
586
587
TEST(HeapSnapshotCollection)588 TEST(HeapSnapshotCollection) {
589 LocalContext env;
590 v8::HandleScope scope(env->GetIsolate());
591 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
592
593 CompileRun(
594 "k = {}; v = {}; s = 'str';\n"
595 "set = new Set(); set.add(k); set.add(v); set[s] = s;\n"
596 "map = new Map(); map.set(k, v); map[s] = s;\n");
597 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
598 CHECK(ValidateSnapshot(snapshot));
599 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
600 const v8::HeapGraphNode* k =
601 GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
602 CHECK(k);
603 const v8::HeapGraphNode* v =
604 GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
605 CHECK(v);
606 const v8::HeapGraphNode* s =
607 GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
608 CHECK(s);
609
610 const v8::HeapGraphNode* set =
611 GetProperty(global, v8::HeapGraphEdge::kProperty, "set");
612 CHECK(set);
613 CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType());
614 CHECK(v8_str("Set")->Equals(env.local(), set->GetName()).FromJust());
615
616 const v8::HeapGraphNode* set_table =
617 GetProperty(set, v8::HeapGraphEdge::kInternal, "table");
618 CHECK_EQ(v8::HeapGraphNode::kArray, set_table->GetType());
619 CHECK_GT(set_table->GetChildrenCount(), 0);
620 int entries = 0;
621 for (int i = 0, count = set_table->GetChildrenCount(); i < count; ++i) {
622 const v8::HeapGraphEdge* prop = set_table->GetChild(i);
623 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
624 if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
625 ++entries;
626 }
627 }
628 CHECK_EQ(2, entries);
629 const v8::HeapGraphNode* set_s =
630 GetProperty(set, v8::HeapGraphEdge::kProperty, "str");
631 CHECK(set_s);
632 CHECK_EQ(s->GetId(), set_s->GetId());
633
634 const v8::HeapGraphNode* map =
635 GetProperty(global, v8::HeapGraphEdge::kProperty, "map");
636 CHECK(map);
637 CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType());
638 CHECK(v8_str("Map")->Equals(env.local(), map->GetName()).FromJust());
639
640 const v8::HeapGraphNode* map_table =
641 GetProperty(map, v8::HeapGraphEdge::kInternal, "table");
642 CHECK_EQ(v8::HeapGraphNode::kArray, map_table->GetType());
643 CHECK_GT(map_table->GetChildrenCount(), 0);
644 entries = 0;
645 for (int i = 0, count = map_table->GetChildrenCount(); i < count; ++i) {
646 const v8::HeapGraphEdge* prop = map_table->GetChild(i);
647 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
648 if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
649 ++entries;
650 }
651 }
652 CHECK_EQ(2, entries);
653 const v8::HeapGraphNode* map_s =
654 GetProperty(map, v8::HeapGraphEdge::kProperty, "str");
655 CHECK(map_s);
656 CHECK_EQ(s->GetId(), map_s->GetId());
657 }
658
659
TEST(HeapSnapshotInternalReferences)660 TEST(HeapSnapshotInternalReferences) {
661 v8::Isolate* isolate = CcTest::isolate();
662 v8::HandleScope scope(isolate);
663 v8::Local<v8::ObjectTemplate> global_template =
664 v8::ObjectTemplate::New(isolate);
665 global_template->SetInternalFieldCount(2);
666 LocalContext env(NULL, global_template);
667 v8::Local<v8::Object> global_proxy = env->Global();
668 v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
669 CHECK_EQ(2, global->InternalFieldCount());
670 v8::Local<v8::Object> obj = v8::Object::New(isolate);
671 global->SetInternalField(0, v8_num(17));
672 global->SetInternalField(1, obj);
673 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
674 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
675 CHECK(ValidateSnapshot(snapshot));
676 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
677 // The first reference will not present, because it's a Smi.
678 CHECK(!GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
679 // The second reference is to an object.
680 CHECK(GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
681 }
682
683
TEST(HeapSnapshotAddressReuse)684 TEST(HeapSnapshotAddressReuse) {
685 LocalContext env;
686 v8::HandleScope scope(env->GetIsolate());
687 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
688
689 CompileRun(
690 "function A() {}\n"
691 "var a = [];\n"
692 "for (var i = 0; i < 10000; ++i)\n"
693 " a[i] = new A();\n");
694 const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
695 CHECK(ValidateSnapshot(snapshot1));
696 v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId();
697
698 CompileRun(
699 "for (var i = 0; i < 10000; ++i)\n"
700 " a[i] = new A();\n");
701 CcTest::heap()->CollectAllGarbage();
702
703 const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
704 CHECK(ValidateSnapshot(snapshot2));
705 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
706
707 const v8::HeapGraphNode* array_node =
708 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
709 CHECK(array_node);
710 int wrong_count = 0;
711 for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) {
712 const v8::HeapGraphEdge* prop = array_node->GetChild(i);
713 if (prop->GetType() != v8::HeapGraphEdge::kElement)
714 continue;
715 v8::SnapshotObjectId id = prop->GetToNode()->GetId();
716 if (id < maxId1)
717 ++wrong_count;
718 }
719 CHECK_EQ(0, wrong_count);
720 }
721
722
TEST(HeapEntryIdsAndArrayShift)723 TEST(HeapEntryIdsAndArrayShift) {
724 LocalContext env;
725 v8::HandleScope scope(env->GetIsolate());
726 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
727
728 CompileRun(
729 "function AnObject() {\n"
730 " this.first = 'first';\n"
731 " this.second = 'second';\n"
732 "}\n"
733 "var a = new Array();\n"
734 "for (var i = 0; i < 10; ++i)\n"
735 " a.push(new AnObject());\n");
736 const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
737 CHECK(ValidateSnapshot(snapshot1));
738
739 CompileRun(
740 "for (var i = 0; i < 1; ++i)\n"
741 " a.shift();\n");
742
743 CcTest::heap()->CollectAllGarbage();
744
745 const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
746 CHECK(ValidateSnapshot(snapshot2));
747
748 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
749 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
750 CHECK_NE(0u, global1->GetId());
751 CHECK_EQ(global1->GetId(), global2->GetId());
752
753 const v8::HeapGraphNode* a1 =
754 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
755 CHECK(a1);
756 const v8::HeapGraphNode* k1 =
757 GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements");
758 CHECK(k1);
759 const v8::HeapGraphNode* a2 =
760 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
761 CHECK(a2);
762 const v8::HeapGraphNode* k2 =
763 GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements");
764 CHECK(k2);
765
766 CHECK_EQ(a1->GetId(), a2->GetId());
767 CHECK_EQ(k1->GetId(), k2->GetId());
768 }
769
770
TEST(HeapEntryIdsAndGC)771 TEST(HeapEntryIdsAndGC) {
772 LocalContext env;
773 v8::HandleScope scope(env->GetIsolate());
774 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
775
776 CompileRun(
777 "function A() {}\n"
778 "function B(x) { this.x = x; }\n"
779 "var a = new A();\n"
780 "var b = new B(a);");
781 const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
782 CHECK(ValidateSnapshot(snapshot1));
783
784 CcTest::heap()->CollectAllGarbage();
785
786 const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
787 CHECK(ValidateSnapshot(snapshot2));
788
789 CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000u);
790 CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
791 snapshot2->GetMaxSnapshotJSObjectId());
792
793 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
794 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
795 CHECK_NE(0u, global1->GetId());
796 CHECK_EQ(global1->GetId(), global2->GetId());
797 const v8::HeapGraphNode* A1 =
798 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
799 CHECK(A1);
800 const v8::HeapGraphNode* A2 =
801 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
802 CHECK(A2);
803 CHECK_NE(0u, A1->GetId());
804 CHECK_EQ(A1->GetId(), A2->GetId());
805 const v8::HeapGraphNode* B1 =
806 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
807 CHECK(B1);
808 const v8::HeapGraphNode* B2 =
809 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
810 CHECK(B2);
811 CHECK_NE(0u, B1->GetId());
812 CHECK_EQ(B1->GetId(), B2->GetId());
813 const v8::HeapGraphNode* a1 =
814 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
815 CHECK(a1);
816 const v8::HeapGraphNode* a2 =
817 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
818 CHECK(a2);
819 CHECK_NE(0u, a1->GetId());
820 CHECK_EQ(a1->GetId(), a2->GetId());
821 const v8::HeapGraphNode* b1 =
822 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
823 CHECK(b1);
824 const v8::HeapGraphNode* b2 =
825 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
826 CHECK(b2);
827 CHECK_NE(0u, b1->GetId());
828 CHECK_EQ(b1->GetId(), b2->GetId());
829 }
830
831
TEST(HeapSnapshotRootPreservedAfterSorting)832 TEST(HeapSnapshotRootPreservedAfterSorting) {
833 LocalContext env;
834 v8::HandleScope scope(env->GetIsolate());
835 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
836 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
837 CHECK(ValidateSnapshot(snapshot));
838 const v8::HeapGraphNode* root1 = snapshot->GetRoot();
839 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
840 snapshot))->GetSortedEntriesList();
841 const v8::HeapGraphNode* root2 = snapshot->GetRoot();
842 CHECK_EQ(root1, root2);
843 }
844
845
846 namespace {
847
848 class TestJSONStream : public v8::OutputStream {
849 public:
TestJSONStream()850 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
TestJSONStream(int abort_countdown)851 explicit TestJSONStream(int abort_countdown)
852 : eos_signaled_(0), abort_countdown_(abort_countdown) {}
~TestJSONStream()853 virtual ~TestJSONStream() {}
EndOfStream()854 virtual void EndOfStream() { ++eos_signaled_; }
WriteAsciiChunk(char * buffer,int chars_written)855 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
856 if (abort_countdown_ > 0) --abort_countdown_;
857 if (abort_countdown_ == 0) return kAbort;
858 CHECK_GT(chars_written, 0);
859 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
860 i::MemCopy(chunk.start(), buffer, chars_written);
861 return kContinue;
862 }
WriteUint32Chunk(uint32_t * buffer,int chars_written)863 virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
864 CHECK(false);
865 return kAbort;
866 }
WriteTo(i::Vector<char> dest)867 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
eos_signaled()868 int eos_signaled() { return eos_signaled_; }
size()869 int size() { return buffer_.size(); }
870
871 private:
872 i::Collector<char> buffer_;
873 int eos_signaled_;
874 int abort_countdown_;
875 };
876
877 class OneByteResource : public v8::String::ExternalOneByteStringResource {
878 public:
OneByteResource(i::Vector<char> string)879 explicit OneByteResource(i::Vector<char> string) : data_(string.start()) {
880 length_ = string.length();
881 }
data() const882 virtual const char* data() const { return data_; }
length() const883 virtual size_t length() const { return length_; }
884 private:
885 const char* data_;
886 size_t length_;
887 };
888
889 } // namespace
890
TEST(HeapSnapshotJSONSerialization)891 TEST(HeapSnapshotJSONSerialization) {
892 v8::Isolate* isolate = CcTest::isolate();
893 LocalContext env;
894 v8::HandleScope scope(isolate);
895 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
896
897 #define STRING_LITERAL_FOR_TEST \
898 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
899 CompileRun(
900 "function A(s) { this.s = s; }\n"
901 "function B(x) { this.x = x; }\n"
902 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
903 "var b = new B(a);");
904 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
905 CHECK(ValidateSnapshot(snapshot));
906
907 TestJSONStream stream;
908 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
909 CHECK_GT(stream.size(), 0);
910 CHECK_EQ(1, stream.eos_signaled());
911 i::ScopedVector<char> json(stream.size());
912 stream.WriteTo(json);
913
914 // Verify that snapshot string is valid JSON.
915 OneByteResource* json_res = new OneByteResource(json);
916 v8::Local<v8::String> json_string =
917 v8::String::NewExternalOneByte(env->GetIsolate(), json_res)
918 .ToLocalChecked();
919 env->Global()
920 ->Set(env.local(), v8_str("json_snapshot"), json_string)
921 .FromJust();
922 v8::Local<v8::Value> snapshot_parse_result = CompileRun(
923 "var parsed = JSON.parse(json_snapshot); true;");
924 CHECK(!snapshot_parse_result.IsEmpty());
925
926 // Verify that snapshot object has required fields.
927 v8::Local<v8::Object> parsed_snapshot =
928 env->Global()
929 ->Get(env.local(), v8_str("parsed"))
930 .ToLocalChecked()
931 ->ToObject(env.local())
932 .ToLocalChecked();
933 CHECK(parsed_snapshot->Has(env.local(), v8_str("snapshot")).FromJust());
934 CHECK(parsed_snapshot->Has(env.local(), v8_str("nodes")).FromJust());
935 CHECK(parsed_snapshot->Has(env.local(), v8_str("edges")).FromJust());
936 CHECK(parsed_snapshot->Has(env.local(), v8_str("strings")).FromJust());
937
938 // Get node and edge "member" offsets.
939 v8::Local<v8::Value> meta_analysis_result = CompileRun(
940 "var meta = parsed.snapshot.meta;\n"
941 "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n"
942 "var node_fields_count = meta.node_fields.length;\n"
943 "var edge_fields_count = meta.edge_fields.length;\n"
944 "var edge_type_offset = meta.edge_fields.indexOf('type');\n"
945 "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
946 "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
947 "var property_type ="
948 " meta.edge_types[edge_type_offset].indexOf('property');\n"
949 "var shortcut_type ="
950 " meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
951 "var node_count = parsed.nodes.length / node_fields_count;\n"
952 "var first_edge_indexes = parsed.first_edge_indexes = [];\n"
953 "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n"
954 " first_edge_indexes[i] = first_edge_index;\n"
955 " first_edge_index += edge_fields_count *\n"
956 " parsed.nodes[i * node_fields_count + edge_count_offset];\n"
957 "}\n"
958 "first_edge_indexes[node_count] = first_edge_index;\n");
959 CHECK(!meta_analysis_result.IsEmpty());
960
961 // A helper function for processing encoded nodes.
962 CompileRun(
963 "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
964 " var nodes = parsed.nodes;\n"
965 " var edges = parsed.edges;\n"
966 " var strings = parsed.strings;\n"
967 " var node_ordinal = pos / node_fields_count;\n"
968 " for (var i = parsed.first_edge_indexes[node_ordinal],\n"
969 " count = parsed.first_edge_indexes[node_ordinal + 1];\n"
970 " i < count; i += edge_fields_count) {\n"
971 " if (edges[i + edge_type_offset] === prop_type\n"
972 " && strings[edges[i + edge_name_offset]] === prop_name)\n"
973 " return edges[i + edge_to_node_offset];\n"
974 " }\n"
975 " return null;\n"
976 "}\n");
977 // Get the string index using the path: <root> -> <global>.b.x.s
978 v8::Local<v8::Value> string_obj_pos_val = CompileRun(
979 "GetChildPosByProperty(\n"
980 " GetChildPosByProperty(\n"
981 " GetChildPosByProperty("
982 " parsed.edges[edge_fields_count + edge_to_node_offset],"
983 " \"b\", property_type),\n"
984 " \"x\", property_type),"
985 " \"s\", property_type)");
986 CHECK(!string_obj_pos_val.IsEmpty());
987 int string_obj_pos = static_cast<int>(
988 string_obj_pos_val->ToNumber(env.local()).ToLocalChecked()->Value());
989 v8::Local<v8::Object> nodes_array =
990 parsed_snapshot->Get(env.local(), v8_str("nodes"))
991 .ToLocalChecked()
992 ->ToObject(env.local())
993 .ToLocalChecked();
994 int string_index =
995 static_cast<int>(nodes_array->Get(env.local(), string_obj_pos + 1)
996 .ToLocalChecked()
997 ->ToNumber(env.local())
998 .ToLocalChecked()
999 ->Value());
1000 CHECK_GT(string_index, 0);
1001 v8::Local<v8::Object> strings_array =
1002 parsed_snapshot->Get(env.local(), v8_str("strings"))
1003 .ToLocalChecked()
1004 ->ToObject(env.local())
1005 .ToLocalChecked();
1006 v8::Local<v8::String> string = strings_array->Get(env.local(), string_index)
1007 .ToLocalChecked()
1008 ->ToString(env.local())
1009 .ToLocalChecked();
1010 v8::Local<v8::String> ref_string = CompileRun(STRING_LITERAL_FOR_TEST)
1011 ->ToString(env.local())
1012 .ToLocalChecked();
1013 #undef STRING_LITERAL_FOR_TEST
1014 CHECK_EQ(0, strcmp(*v8::String::Utf8Value(ref_string),
1015 *v8::String::Utf8Value(string)));
1016 }
1017
1018
TEST(HeapSnapshotJSONSerializationAborting)1019 TEST(HeapSnapshotJSONSerializationAborting) {
1020 LocalContext env;
1021 v8::HandleScope scope(env->GetIsolate());
1022 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1023 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1024 CHECK(ValidateSnapshot(snapshot));
1025 TestJSONStream stream(5);
1026 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1027 CHECK_GT(stream.size(), 0);
1028 CHECK_EQ(0, stream.eos_signaled());
1029 }
1030
1031 namespace {
1032
1033 class TestStatsStream : public v8::OutputStream {
1034 public:
TestStatsStream()1035 TestStatsStream()
1036 : eos_signaled_(0),
1037 updates_written_(0),
1038 entries_count_(0),
1039 entries_size_(0),
1040 intervals_count_(0),
1041 first_interval_index_(-1) { }
TestStatsStream(const TestStatsStream & stream)1042 TestStatsStream(const TestStatsStream& stream)
1043 : v8::OutputStream(stream),
1044 eos_signaled_(stream.eos_signaled_),
1045 updates_written_(stream.updates_written_),
1046 entries_count_(stream.entries_count_),
1047 entries_size_(stream.entries_size_),
1048 intervals_count_(stream.intervals_count_),
1049 first_interval_index_(stream.first_interval_index_) { }
~TestStatsStream()1050 virtual ~TestStatsStream() {}
EndOfStream()1051 virtual void EndOfStream() { ++eos_signaled_; }
WriteAsciiChunk(char * buffer,int chars_written)1052 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
1053 CHECK(false);
1054 return kAbort;
1055 }
WriteHeapStatsChunk(v8::HeapStatsUpdate * buffer,int updates_written)1056 virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
1057 int updates_written) {
1058 ++intervals_count_;
1059 CHECK(updates_written);
1060 updates_written_ += updates_written;
1061 entries_count_ = 0;
1062 if (first_interval_index_ == -1 && updates_written != 0)
1063 first_interval_index_ = buffer[0].index;
1064 for (int i = 0; i < updates_written; ++i) {
1065 entries_count_ += buffer[i].count;
1066 entries_size_ += buffer[i].size;
1067 }
1068
1069 return kContinue;
1070 }
eos_signaled()1071 int eos_signaled() { return eos_signaled_; }
updates_written()1072 int updates_written() { return updates_written_; }
entries_count() const1073 uint32_t entries_count() const { return entries_count_; }
entries_size() const1074 uint32_t entries_size() const { return entries_size_; }
intervals_count() const1075 int intervals_count() const { return intervals_count_; }
first_interval_index() const1076 int first_interval_index() const { return first_interval_index_; }
1077
1078 private:
1079 int eos_signaled_;
1080 int updates_written_;
1081 uint32_t entries_count_;
1082 uint32_t entries_size_;
1083 int intervals_count_;
1084 int first_interval_index_;
1085 };
1086
1087 } // namespace
1088
GetHeapStatsUpdate(v8::HeapProfiler * heap_profiler,v8::SnapshotObjectId * object_id=NULL)1089 static TestStatsStream GetHeapStatsUpdate(
1090 v8::HeapProfiler* heap_profiler,
1091 v8::SnapshotObjectId* object_id = NULL) {
1092 TestStatsStream stream;
1093 int64_t timestamp = -1;
1094 v8::SnapshotObjectId last_seen_id =
1095 heap_profiler->GetHeapStats(&stream, ×tamp);
1096 if (object_id)
1097 *object_id = last_seen_id;
1098 CHECK_NE(-1, timestamp);
1099 CHECK_EQ(1, stream.eos_signaled());
1100 return stream;
1101 }
1102
1103
TEST(HeapSnapshotObjectsStats)1104 TEST(HeapSnapshotObjectsStats) {
1105 LocalContext env;
1106 v8::HandleScope scope(env->GetIsolate());
1107 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1108
1109 heap_profiler->StartTrackingHeapObjects();
1110 // We have to call GC 6 times. In other case the garbage will be
1111 // the reason of flakiness.
1112 for (int i = 0; i < 6; ++i) {
1113 CcTest::heap()->CollectAllGarbage();
1114 }
1115
1116 v8::SnapshotObjectId initial_id;
1117 {
1118 // Single chunk of data expected in update. Initial data.
1119 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
1120 &initial_id);
1121 CHECK_EQ(1, stats_update.intervals_count());
1122 CHECK_EQ(1, stats_update.updates_written());
1123 CHECK_LT(0u, stats_update.entries_size());
1124 CHECK_EQ(0, stats_update.first_interval_index());
1125 }
1126
1127 // No data expected in update because nothing has happened.
1128 v8::SnapshotObjectId same_id;
1129 CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written());
1130 CHECK_EQ(initial_id, same_id);
1131
1132 {
1133 v8::SnapshotObjectId additional_string_id;
1134 v8::HandleScope inner_scope_1(env->GetIsolate());
1135 v8_str("string1");
1136 {
1137 // Single chunk of data with one new entry expected in update.
1138 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
1139 &additional_string_id);
1140 CHECK_LT(same_id, additional_string_id);
1141 CHECK_EQ(1, stats_update.intervals_count());
1142 CHECK_EQ(1, stats_update.updates_written());
1143 CHECK_LT(0u, stats_update.entries_size());
1144 CHECK_EQ(1u, stats_update.entries_count());
1145 CHECK_EQ(2, stats_update.first_interval_index());
1146 }
1147
1148 // No data expected in update because nothing happened.
1149 v8::SnapshotObjectId last_id;
1150 CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written());
1151 CHECK_EQ(additional_string_id, last_id);
1152
1153 {
1154 v8::HandleScope inner_scope_2(env->GetIsolate());
1155 v8_str("string2");
1156
1157 uint32_t entries_size;
1158 {
1159 v8::HandleScope inner_scope_3(env->GetIsolate());
1160 v8_str("string3");
1161 v8_str("string4");
1162
1163 {
1164 // Single chunk of data with three new entries expected in update.
1165 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1166 CHECK_EQ(1, stats_update.intervals_count());
1167 CHECK_EQ(1, stats_update.updates_written());
1168 CHECK_LT(0u, entries_size = stats_update.entries_size());
1169 CHECK_EQ(3u, stats_update.entries_count());
1170 CHECK_EQ(4, stats_update.first_interval_index());
1171 }
1172 }
1173
1174 {
1175 // Single chunk of data with two left entries expected in update.
1176 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1177 CHECK_EQ(1, stats_update.intervals_count());
1178 CHECK_EQ(1, stats_update.updates_written());
1179 CHECK_GT(entries_size, stats_update.entries_size());
1180 CHECK_EQ(1u, stats_update.entries_count());
1181 // Two strings from forth interval were released.
1182 CHECK_EQ(4, stats_update.first_interval_index());
1183 }
1184 }
1185
1186 {
1187 // Single chunk of data with 0 left entries expected in update.
1188 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1189 CHECK_EQ(1, stats_update.intervals_count());
1190 CHECK_EQ(1, stats_update.updates_written());
1191 CHECK_EQ(0u, stats_update.entries_size());
1192 CHECK_EQ(0u, stats_update.entries_count());
1193 // The last string from forth interval was released.
1194 CHECK_EQ(4, stats_update.first_interval_index());
1195 }
1196 }
1197 {
1198 // Single chunk of data with 0 left entries expected in update.
1199 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1200 CHECK_EQ(1, stats_update.intervals_count());
1201 CHECK_EQ(1, stats_update.updates_written());
1202 CHECK_EQ(0u, stats_update.entries_size());
1203 CHECK_EQ(0u, stats_update.entries_count());
1204 // The only string from the second interval was released.
1205 CHECK_EQ(2, stats_update.first_interval_index());
1206 }
1207
1208 v8::Local<v8::Array> array = v8::Array::New(env->GetIsolate());
1209 CHECK_EQ(0u, array->Length());
1210 // Force array's buffer allocation.
1211 array->Set(env.local(), 2, v8_num(7)).FromJust();
1212
1213 uint32_t entries_size;
1214 {
1215 // Single chunk of data with 2 entries expected in update.
1216 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1217 CHECK_EQ(1, stats_update.intervals_count());
1218 CHECK_EQ(1, stats_update.updates_written());
1219 CHECK_LT(0u, entries_size = stats_update.entries_size());
1220 // They are the array and its buffer.
1221 CHECK_EQ(2u, stats_update.entries_count());
1222 CHECK_EQ(8, stats_update.first_interval_index());
1223 }
1224
1225 for (int i = 0; i < 100; ++i)
1226 array->Set(env.local(), i, v8_num(i)).FromJust();
1227
1228 {
1229 // Single chunk of data with 1 entry expected in update.
1230 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1231 CHECK_EQ(1, stats_update.intervals_count());
1232 // The first interval was changed because old buffer was collected.
1233 // The second interval was changed because new buffer was allocated.
1234 CHECK_EQ(2, stats_update.updates_written());
1235 CHECK_LT(entries_size, stats_update.entries_size());
1236 CHECK_EQ(2u, stats_update.entries_count());
1237 CHECK_EQ(8, stats_update.first_interval_index());
1238 }
1239
1240 heap_profiler->StopTrackingHeapObjects();
1241 }
1242
1243
TEST(HeapObjectIds)1244 TEST(HeapObjectIds) {
1245 LocalContext env;
1246 v8::Isolate* isolate = env->GetIsolate();
1247 v8::HandleScope scope(isolate);
1248 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1249
1250 const int kLength = 10;
1251 v8::Local<v8::Object> objects[kLength];
1252 v8::SnapshotObjectId ids[kLength];
1253
1254 heap_profiler->StartTrackingHeapObjects(false);
1255
1256 for (int i = 0; i < kLength; i++) {
1257 objects[i] = v8::Object::New(isolate);
1258 }
1259 GetHeapStatsUpdate(heap_profiler);
1260
1261 for (int i = 0; i < kLength; i++) {
1262 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1263 CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
1264 ids[i] = id;
1265 }
1266
1267 heap_profiler->StopTrackingHeapObjects();
1268 CcTest::heap()->CollectAllAvailableGarbage();
1269
1270 for (int i = 0; i < kLength; i++) {
1271 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1272 CHECK_EQ(ids[i], id);
1273 v8::Local<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
1274 CHECK(objects[i]->Equals(env.local(), obj).FromJust());
1275 }
1276
1277 heap_profiler->ClearObjectIds();
1278 for (int i = 0; i < kLength; i++) {
1279 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1280 CHECK_EQ(v8::HeapProfiler::kUnknownObjectId, id);
1281 v8::Local<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
1282 CHECK(obj.IsEmpty());
1283 }
1284 }
1285
1286
CheckChildrenIds(const v8::HeapSnapshot * snapshot,const v8::HeapGraphNode * node,int level,int max_level)1287 static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
1288 const v8::HeapGraphNode* node,
1289 int level, int max_level) {
1290 if (level > max_level) return;
1291 CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
1292 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
1293 const v8::HeapGraphEdge* prop = node->GetChild(i);
1294 const v8::HeapGraphNode* child =
1295 snapshot->GetNodeById(prop->GetToNode()->GetId());
1296 CHECK_EQ(prop->GetToNode()->GetId(), child->GetId());
1297 CHECK_EQ(prop->GetToNode(), child);
1298 CheckChildrenIds(snapshot, child, level + 1, max_level);
1299 }
1300 }
1301
1302
TEST(HeapSnapshotGetNodeById)1303 TEST(HeapSnapshotGetNodeById) {
1304 LocalContext env;
1305 v8::HandleScope scope(env->GetIsolate());
1306 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1307
1308 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1309 CHECK(ValidateSnapshot(snapshot));
1310 const v8::HeapGraphNode* root = snapshot->GetRoot();
1311 CheckChildrenIds(snapshot, root, 0, 3);
1312 // Check a big id, which should not exist yet.
1313 CHECK(!snapshot->GetNodeById(0x1000000UL));
1314 }
1315
1316
TEST(HeapSnapshotGetSnapshotObjectId)1317 TEST(HeapSnapshotGetSnapshotObjectId) {
1318 LocalContext env;
1319 v8::HandleScope scope(env->GetIsolate());
1320 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1321 CompileRun("globalObject = {};\n");
1322 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1323 CHECK(ValidateSnapshot(snapshot));
1324 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1325 const v8::HeapGraphNode* global_object =
1326 GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject");
1327 CHECK(global_object);
1328
1329 v8::Local<v8::Value> globalObjectHandle =
1330 env->Global()->Get(env.local(), v8_str("globalObject")).ToLocalChecked();
1331 CHECK(!globalObjectHandle.IsEmpty());
1332 CHECK(globalObjectHandle->IsObject());
1333
1334 v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle);
1335 CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
1336 CHECK_EQ(id, global_object->GetId());
1337 }
1338
1339
TEST(HeapSnapshotUnknownSnapshotObjectId)1340 TEST(HeapSnapshotUnknownSnapshotObjectId) {
1341 LocalContext env;
1342 v8::HandleScope scope(env->GetIsolate());
1343 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1344 CompileRun("globalObject = {};\n");
1345 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1346 CHECK(ValidateSnapshot(snapshot));
1347 const v8::HeapGraphNode* node =
1348 snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
1349 CHECK(!node);
1350 }
1351
1352
1353 namespace {
1354
1355 class TestActivityControl : public v8::ActivityControl {
1356 public:
TestActivityControl(int abort_count)1357 explicit TestActivityControl(int abort_count)
1358 : done_(0), total_(0), abort_count_(abort_count) {}
ReportProgressValue(int done,int total)1359 ControlOption ReportProgressValue(int done, int total) {
1360 done_ = done;
1361 total_ = total;
1362 return --abort_count_ != 0 ? kContinue : kAbort;
1363 }
done()1364 int done() { return done_; }
total()1365 int total() { return total_; }
1366
1367 private:
1368 int done_;
1369 int total_;
1370 int abort_count_;
1371 };
1372
1373 } // namespace
1374
1375
TEST(TakeHeapSnapshotAborting)1376 TEST(TakeHeapSnapshotAborting) {
1377 LocalContext env;
1378 v8::HandleScope scope(env->GetIsolate());
1379
1380 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1381 const int snapshots_count = heap_profiler->GetSnapshotCount();
1382 TestActivityControl aborting_control(1);
1383 const v8::HeapSnapshot* no_snapshot =
1384 heap_profiler->TakeHeapSnapshot(&aborting_control);
1385 CHECK(!no_snapshot);
1386 CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount());
1387 CHECK_GT(aborting_control.total(), aborting_control.done());
1388
1389 TestActivityControl control(-1); // Don't abort.
1390 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(&control);
1391 CHECK(ValidateSnapshot(snapshot));
1392
1393 CHECK(snapshot);
1394 CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount());
1395 CHECK_EQ(control.total(), control.done());
1396 CHECK_GT(control.total(), 0);
1397 }
1398
1399
1400 namespace {
1401
1402 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
1403 public:
TestRetainedObjectInfo(int hash,const char * group_label,const char * label,intptr_t element_count=-1,intptr_t size=-1)1404 TestRetainedObjectInfo(int hash,
1405 const char* group_label,
1406 const char* label,
1407 intptr_t element_count = -1,
1408 intptr_t size = -1)
1409 : disposed_(false),
1410 hash_(hash),
1411 group_label_(group_label),
1412 label_(label),
1413 element_count_(element_count),
1414 size_(size) {
1415 instances.Add(this);
1416 }
~TestRetainedObjectInfo()1417 virtual ~TestRetainedObjectInfo() {}
Dispose()1418 virtual void Dispose() {
1419 CHECK(!disposed_);
1420 disposed_ = true;
1421 }
IsEquivalent(RetainedObjectInfo * other)1422 virtual bool IsEquivalent(RetainedObjectInfo* other) {
1423 return GetHash() == other->GetHash();
1424 }
GetHash()1425 virtual intptr_t GetHash() { return hash_; }
GetGroupLabel()1426 virtual const char* GetGroupLabel() { return group_label_; }
GetLabel()1427 virtual const char* GetLabel() { return label_; }
GetElementCount()1428 virtual intptr_t GetElementCount() { return element_count_; }
GetSizeInBytes()1429 virtual intptr_t GetSizeInBytes() { return size_; }
disposed()1430 bool disposed() { return disposed_; }
1431
WrapperInfoCallback(uint16_t class_id,v8::Local<v8::Value> wrapper)1432 static v8::RetainedObjectInfo* WrapperInfoCallback(
1433 uint16_t class_id, v8::Local<v8::Value> wrapper) {
1434 if (class_id == 1) {
1435 if (wrapper->IsString()) {
1436 v8::String::Utf8Value utf8(wrapper);
1437 if (strcmp(*utf8, "AAA") == 0)
1438 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1439 else if (strcmp(*utf8, "BBB") == 0)
1440 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1441 }
1442 } else if (class_id == 2) {
1443 if (wrapper->IsString()) {
1444 v8::String::Utf8Value utf8(wrapper);
1445 if (strcmp(*utf8, "CCC") == 0)
1446 return new TestRetainedObjectInfo(2, "ccc-group", "ccc");
1447 }
1448 }
1449 CHECK(false);
1450 return NULL;
1451 }
1452
1453 static i::List<TestRetainedObjectInfo*> instances;
1454
1455 private:
1456 bool disposed_;
1457 int hash_;
1458 const char* group_label_;
1459 const char* label_;
1460 intptr_t element_count_;
1461 intptr_t size_;
1462 };
1463
1464
1465 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
1466
1467 } // namespace
1468
1469
GetNode(const v8::HeapGraphNode * parent,v8::HeapGraphNode::Type type,const char * name)1470 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
1471 v8::HeapGraphNode::Type type,
1472 const char* name) {
1473 for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
1474 const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
1475 if (node->GetType() == type && strcmp(name,
1476 const_cast<i::HeapEntry*>(
1477 reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
1478 return node;
1479 }
1480 }
1481 return NULL;
1482 }
1483
1484
TEST(HeapSnapshotRetainedObjectInfo)1485 TEST(HeapSnapshotRetainedObjectInfo) {
1486 LocalContext env;
1487 v8::Isolate* isolate = env->GetIsolate();
1488 v8::HandleScope scope(isolate);
1489 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1490
1491 heap_profiler->SetWrapperClassInfoProvider(
1492 1, TestRetainedObjectInfo::WrapperInfoCallback);
1493 heap_profiler->SetWrapperClassInfoProvider(
1494 2, TestRetainedObjectInfo::WrapperInfoCallback);
1495 v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
1496 p_AAA.SetWrapperClassId(1);
1497 v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
1498 p_BBB.SetWrapperClassId(1);
1499 v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC"));
1500 p_CCC.SetWrapperClassId(2);
1501 CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
1502 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1503 CHECK(ValidateSnapshot(snapshot));
1504
1505 CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
1506 for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
1507 CHECK(TestRetainedObjectInfo::instances[i]->disposed());
1508 delete TestRetainedObjectInfo::instances[i];
1509 }
1510
1511 const v8::HeapGraphNode* native_group_aaa = GetNode(
1512 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
1513 CHECK(native_group_aaa);
1514 CHECK_EQ(1, native_group_aaa->GetChildrenCount());
1515 const v8::HeapGraphNode* aaa = GetNode(
1516 native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries");
1517 CHECK(aaa);
1518 CHECK_EQ(2, aaa->GetChildrenCount());
1519
1520 const v8::HeapGraphNode* native_group_ccc = GetNode(
1521 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
1522 const v8::HeapGraphNode* ccc = GetNode(
1523 native_group_ccc, v8::HeapGraphNode::kNative, "ccc");
1524 CHECK(ccc);
1525
1526 const v8::HeapGraphNode* n_AAA = GetNode(
1527 aaa, v8::HeapGraphNode::kString, "AAA");
1528 CHECK(n_AAA);
1529 const v8::HeapGraphNode* n_BBB = GetNode(
1530 aaa, v8::HeapGraphNode::kString, "BBB");
1531 CHECK(n_BBB);
1532 CHECK_EQ(1, ccc->GetChildrenCount());
1533 const v8::HeapGraphNode* n_CCC = GetNode(
1534 ccc, v8::HeapGraphNode::kString, "CCC");
1535 CHECK(n_CCC);
1536
1537 CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
1538 CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
1539 CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
1540 }
1541
1542
1543 class GraphWithImplicitRefs {
1544 public:
1545 static const int kObjectsCount = 4;
GraphWithImplicitRefs(LocalContext * env)1546 explicit GraphWithImplicitRefs(LocalContext* env) {
1547 CHECK(!instance_);
1548 instance_ = this;
1549 isolate_ = (*env)->GetIsolate();
1550 for (int i = 0; i < kObjectsCount; i++) {
1551 objects_[i].Reset(isolate_, v8::Object::New(isolate_));
1552 }
1553 (*env)
1554 ->Global()
1555 ->Set(isolate_->GetCurrentContext(), v8_str("root_object"),
1556 v8::Local<v8::Value>::New(isolate_, objects_[0]))
1557 .FromJust();
1558 }
~GraphWithImplicitRefs()1559 ~GraphWithImplicitRefs() {
1560 instance_ = NULL;
1561 }
1562
gcPrologue(v8::Isolate * isolate,v8::GCType type,v8::GCCallbackFlags flags)1563 static void gcPrologue(v8::Isolate* isolate, v8::GCType type,
1564 v8::GCCallbackFlags flags) {
1565 instance_->AddImplicitReferences();
1566 }
1567
1568 private:
AddImplicitReferences()1569 void AddImplicitReferences() {
1570 // 0 -> 1
1571 isolate_->SetObjectGroupId(objects_[0],
1572 v8::UniqueId(1));
1573 isolate_->SetReferenceFromGroup(
1574 v8::UniqueId(1), objects_[1]);
1575 // Adding two more references: 1 -> 2, 1 -> 3
1576 isolate_->SetReference(objects_[1].As<v8::Object>(),
1577 objects_[2]);
1578 isolate_->SetReference(objects_[1].As<v8::Object>(),
1579 objects_[3]);
1580 }
1581
1582 v8::Persistent<v8::Value> objects_[kObjectsCount];
1583 static GraphWithImplicitRefs* instance_;
1584 v8::Isolate* isolate_;
1585 };
1586
1587 GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL;
1588
1589
TEST(HeapSnapshotImplicitReferences)1590 TEST(HeapSnapshotImplicitReferences) {
1591 LocalContext env;
1592 v8::HandleScope scope(env->GetIsolate());
1593 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1594
1595 GraphWithImplicitRefs graph(&env);
1596 env->GetIsolate()->AddGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1597
1598 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1599 CHECK(ValidateSnapshot(snapshot));
1600
1601 const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot);
1602 const v8::HeapGraphNode* obj0 = GetProperty(
1603 global_object, v8::HeapGraphEdge::kProperty, "root_object");
1604 CHECK(obj0);
1605 CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType());
1606 const v8::HeapGraphNode* obj1 = GetProperty(
1607 obj0, v8::HeapGraphEdge::kInternal, "native");
1608 CHECK(obj1);
1609 int implicit_targets_count = 0;
1610 for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) {
1611 const v8::HeapGraphEdge* prop = obj1->GetChild(i);
1612 v8::String::Utf8Value prop_name(prop->GetName());
1613 if (prop->GetType() == v8::HeapGraphEdge::kInternal &&
1614 strcmp("native", *prop_name) == 0) {
1615 ++implicit_targets_count;
1616 }
1617 }
1618 CHECK_EQ(2, implicit_targets_count);
1619 env->GetIsolate()->RemoveGCPrologueCallback(
1620 &GraphWithImplicitRefs::gcPrologue);
1621 }
1622
1623
TEST(DeleteAllHeapSnapshots)1624 TEST(DeleteAllHeapSnapshots) {
1625 LocalContext env;
1626 v8::HandleScope scope(env->GetIsolate());
1627 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1628
1629 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1630 heap_profiler->DeleteAllHeapSnapshots();
1631 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1632 CHECK(heap_profiler->TakeHeapSnapshot());
1633 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1634 heap_profiler->DeleteAllHeapSnapshots();
1635 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1636 CHECK(heap_profiler->TakeHeapSnapshot());
1637 CHECK(heap_profiler->TakeHeapSnapshot());
1638 CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1639 heap_profiler->DeleteAllHeapSnapshots();
1640 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1641 }
1642
1643
FindHeapSnapshot(v8::HeapProfiler * profiler,const v8::HeapSnapshot * snapshot)1644 static bool FindHeapSnapshot(v8::HeapProfiler* profiler,
1645 const v8::HeapSnapshot* snapshot) {
1646 int length = profiler->GetSnapshotCount();
1647 for (int i = 0; i < length; i++) {
1648 if (snapshot == profiler->GetHeapSnapshot(i)) return true;
1649 }
1650 return false;
1651 }
1652
1653
TEST(DeleteHeapSnapshot)1654 TEST(DeleteHeapSnapshot) {
1655 LocalContext env;
1656 v8::HandleScope scope(env->GetIsolate());
1657 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1658
1659 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1660 const v8::HeapSnapshot* s1 = heap_profiler->TakeHeapSnapshot();
1661
1662 CHECK(s1);
1663 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1664 CHECK(FindHeapSnapshot(heap_profiler, s1));
1665 const_cast<v8::HeapSnapshot*>(s1)->Delete();
1666 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1667 CHECK(!FindHeapSnapshot(heap_profiler, s1));
1668
1669 const v8::HeapSnapshot* s2 = heap_profiler->TakeHeapSnapshot();
1670 CHECK(s2);
1671 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1672 CHECK(FindHeapSnapshot(heap_profiler, s2));
1673 const v8::HeapSnapshot* s3 = heap_profiler->TakeHeapSnapshot();
1674 CHECK(s3);
1675 CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1676 CHECK_NE(s2, s3);
1677 CHECK(FindHeapSnapshot(heap_profiler, s3));
1678 const_cast<v8::HeapSnapshot*>(s2)->Delete();
1679 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1680 CHECK(!FindHeapSnapshot(heap_profiler, s2));
1681 CHECK(FindHeapSnapshot(heap_profiler, s3));
1682 const_cast<v8::HeapSnapshot*>(s3)->Delete();
1683 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1684 CHECK(!FindHeapSnapshot(heap_profiler, s3));
1685 }
1686
1687
1688 class NameResolver : public v8::HeapProfiler::ObjectNameResolver {
1689 public:
GetName(v8::Local<v8::Object> object)1690 virtual const char* GetName(v8::Local<v8::Object> object) {
1691 return "Global object name";
1692 }
1693 };
1694
1695
TEST(GlobalObjectName)1696 TEST(GlobalObjectName) {
1697 LocalContext env;
1698 v8::HandleScope scope(env->GetIsolate());
1699 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1700
1701 CompileRun("document = { URL:\"abcdefgh\" };");
1702
1703 NameResolver name_resolver;
1704 const v8::HeapSnapshot* snapshot =
1705 heap_profiler->TakeHeapSnapshot(NULL, &name_resolver);
1706 CHECK(ValidateSnapshot(snapshot));
1707 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1708 CHECK(global);
1709 CHECK_EQ(0,
1710 strcmp("Object / Global object name",
1711 const_cast<i::HeapEntry*>(
1712 reinterpret_cast<const i::HeapEntry*>(global))->name()));
1713 }
1714
1715
TEST(GlobalObjectFields)1716 TEST(GlobalObjectFields) {
1717 LocalContext env;
1718 v8::HandleScope scope(env->GetIsolate());
1719 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1720 CompileRun("obj = {};");
1721 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1722 CHECK(ValidateSnapshot(snapshot));
1723 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1724 const v8::HeapGraphNode* native_context =
1725 GetProperty(global, v8::HeapGraphEdge::kInternal, "native_context");
1726 CHECK(native_context);
1727 const v8::HeapGraphNode* global_proxy =
1728 GetProperty(global, v8::HeapGraphEdge::kInternal, "global_proxy");
1729 CHECK(global_proxy);
1730 }
1731
1732
TEST(NoHandleLeaks)1733 TEST(NoHandleLeaks) {
1734 LocalContext env;
1735 v8::HandleScope scope(env->GetIsolate());
1736 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1737
1738 CompileRun("document = { URL:\"abcdefgh\" };");
1739
1740 i::Isolate* isolate = CcTest::i_isolate();
1741 int count_before = i::HandleScope::NumberOfHandles(isolate);
1742 heap_profiler->TakeHeapSnapshot();
1743 int count_after = i::HandleScope::NumberOfHandles(isolate);
1744 CHECK_EQ(count_before, count_after);
1745 }
1746
1747
TEST(NodesIteration)1748 TEST(NodesIteration) {
1749 LocalContext env;
1750 v8::HandleScope scope(env->GetIsolate());
1751 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1752 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1753 CHECK(ValidateSnapshot(snapshot));
1754 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1755 CHECK(global);
1756 // Verify that we can find this object by iteration.
1757 const int nodes_count = snapshot->GetNodesCount();
1758 int count = 0;
1759 for (int i = 0; i < nodes_count; ++i) {
1760 if (snapshot->GetNode(i) == global)
1761 ++count;
1762 }
1763 CHECK_EQ(1, count);
1764 }
1765
1766
TEST(GetHeapValueForNode)1767 TEST(GetHeapValueForNode) {
1768 LocalContext env;
1769 v8::HandleScope scope(env->GetIsolate());
1770 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1771
1772 CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
1773 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1774 CHECK(ValidateSnapshot(snapshot));
1775 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1776 CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject());
1777 v8::Local<v8::Object> js_global =
1778 env->Global()->GetPrototype().As<v8::Object>();
1779 CHECK(js_global == heap_profiler->FindObjectById(global->GetId()));
1780 const v8::HeapGraphNode* obj = GetProperty(
1781 global, v8::HeapGraphEdge::kProperty, "a");
1782 CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject());
1783 v8::Local<v8::Object> js_obj = js_global->Get(env.local(), v8_str("a"))
1784 .ToLocalChecked()
1785 .As<v8::Object>();
1786 CHECK(js_obj == heap_profiler->FindObjectById(obj->GetId()));
1787 const v8::HeapGraphNode* s_prop =
1788 GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
1789 v8::Local<v8::String> js_s_prop = js_obj->Get(env.local(), v8_str("s_prop"))
1790 .ToLocalChecked()
1791 .As<v8::String>();
1792 CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId()));
1793 const v8::HeapGraphNode* n_prop =
1794 GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
1795 v8::Local<v8::String> js_n_prop = js_obj->Get(env.local(), v8_str("n_prop"))
1796 .ToLocalChecked()
1797 .As<v8::String>();
1798 CHECK(js_n_prop == heap_profiler->FindObjectById(n_prop->GetId()));
1799 }
1800
1801
TEST(GetHeapValueForDeletedObject)1802 TEST(GetHeapValueForDeletedObject) {
1803 LocalContext env;
1804 v8::HandleScope scope(env->GetIsolate());
1805 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1806
1807 // It is impossible to delete a global property, so we are about to delete a
1808 // property of the "a" object. Also, the "p" object can't be an empty one
1809 // because the empty object is static and isn't actually deleted.
1810 CompileRun("a = { p: { r: {} } };");
1811 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1812 CHECK(ValidateSnapshot(snapshot));
1813 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1814 const v8::HeapGraphNode* obj = GetProperty(
1815 global, v8::HeapGraphEdge::kProperty, "a");
1816 const v8::HeapGraphNode* prop = GetProperty(
1817 obj, v8::HeapGraphEdge::kProperty, "p");
1818 {
1819 // Perform the check inside a nested local scope to avoid creating a
1820 // reference to the object we are deleting.
1821 v8::HandleScope scope(env->GetIsolate());
1822 CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject());
1823 }
1824 CompileRun("delete a.p;");
1825 CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty());
1826 }
1827
1828
StringCmp(const char * ref,i::String * act)1829 static int StringCmp(const char* ref, i::String* act) {
1830 v8::base::SmartArrayPointer<char> s_act = act->ToCString();
1831 int result = strcmp(ref, s_act.get());
1832 if (result != 0)
1833 fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get());
1834 return result;
1835 }
1836
1837
TEST(GetConstructorName)1838 TEST(GetConstructorName) {
1839 LocalContext env;
1840 v8::HandleScope scope(env->GetIsolate());
1841
1842 CompileRun(
1843 "function Constructor1() {};\n"
1844 "var obj1 = new Constructor1();\n"
1845 "var Constructor2 = function() {};\n"
1846 "var obj2 = new Constructor2();\n"
1847 "var obj3 = {};\n"
1848 "obj3.__proto__ = { constructor: function Constructor3() {} };\n"
1849 "var obj4 = {};\n"
1850 "// Slow properties\n"
1851 "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1852 "obj4.__proto__ = { constructor: function Constructor4() {} };\n"
1853 "var obj5 = {};\n"
1854 "var obj6 = {};\n"
1855 "obj6.constructor = 6;");
1856 v8::Local<v8::Object> js_global =
1857 env->Global()->GetPrototype().As<v8::Object>();
1858 v8::Local<v8::Object> obj1 = js_global->Get(env.local(), v8_str("obj1"))
1859 .ToLocalChecked()
1860 .As<v8::Object>();
1861 i::Handle<i::JSObject> js_obj1 =
1862 i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj1));
1863 CHECK_EQ(0, StringCmp(
1864 "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
1865 v8::Local<v8::Object> obj2 = js_global->Get(env.local(), v8_str("obj2"))
1866 .ToLocalChecked()
1867 .As<v8::Object>();
1868 i::Handle<i::JSObject> js_obj2 =
1869 i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj2));
1870 CHECK_EQ(0, StringCmp(
1871 "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
1872 v8::Local<v8::Object> obj3 = js_global->Get(env.local(), v8_str("obj3"))
1873 .ToLocalChecked()
1874 .As<v8::Object>();
1875 i::Handle<i::JSObject> js_obj3 =
1876 i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj3));
1877 CHECK_EQ(0, StringCmp("Constructor3",
1878 i::V8HeapExplorer::GetConstructorName(*js_obj3)));
1879 v8::Local<v8::Object> obj4 = js_global->Get(env.local(), v8_str("obj4"))
1880 .ToLocalChecked()
1881 .As<v8::Object>();
1882 i::Handle<i::JSObject> js_obj4 =
1883 i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj4));
1884 CHECK_EQ(0, StringCmp("Constructor4",
1885 i::V8HeapExplorer::GetConstructorName(*js_obj4)));
1886 v8::Local<v8::Object> obj5 = js_global->Get(env.local(), v8_str("obj5"))
1887 .ToLocalChecked()
1888 .As<v8::Object>();
1889 i::Handle<i::JSObject> js_obj5 =
1890 i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj5));
1891 CHECK_EQ(0, StringCmp(
1892 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
1893 v8::Local<v8::Object> obj6 = js_global->Get(env.local(), v8_str("obj6"))
1894 .ToLocalChecked()
1895 .As<v8::Object>();
1896 i::Handle<i::JSObject> js_obj6 =
1897 i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj6));
1898 CHECK_EQ(0, StringCmp(
1899 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
1900 }
1901
1902
TEST(FastCaseAccessors)1903 TEST(FastCaseAccessors) {
1904 LocalContext env;
1905 v8::HandleScope scope(env->GetIsolate());
1906 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1907
1908 CompileRun("var obj1 = {};\n"
1909 "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1910 " return 42;\n"
1911 "});\n"
1912 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1913 " return this.value_ = value;\n"
1914 "});\n");
1915 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1916 CHECK(ValidateSnapshot(snapshot));
1917
1918 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1919 CHECK(global);
1920 const v8::HeapGraphNode* obj1 =
1921 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1922 CHECK(obj1);
1923 const v8::HeapGraphNode* func;
1924 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
1925 CHECK(func);
1926 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
1927 CHECK(!func);
1928 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
1929 CHECK(func);
1930 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
1931 CHECK(!func);
1932 }
1933
1934
TEST(FastCaseRedefinedAccessors)1935 TEST(FastCaseRedefinedAccessors) {
1936 LocalContext env;
1937 v8::HandleScope scope(env->GetIsolate());
1938 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1939
1940 CompileRun(
1941 "var obj1 = {};\n"
1942 "Object.defineProperty(obj1, 'prop', { "
1943 " get: function() { return 42; },\n"
1944 " set: function(value) { return this.prop_ = value; },\n"
1945 " configurable: true,\n"
1946 " enumerable: true,\n"
1947 "});\n"
1948 "Object.defineProperty(obj1, 'prop', { "
1949 " get: function() { return 153; },\n"
1950 " set: function(value) { return this.prop_ = value; },\n"
1951 " configurable: true,\n"
1952 " enumerable: true,\n"
1953 "});\n");
1954 v8::Local<v8::Object> js_global =
1955 env->Global()->GetPrototype().As<v8::Object>();
1956 i::Handle<i::JSReceiver> js_obj1 =
1957 v8::Utils::OpenHandle(*js_global->Get(env.local(), v8_str("obj1"))
1958 .ToLocalChecked()
1959 .As<v8::Object>());
1960 USE(js_obj1);
1961
1962 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1963 CHECK(ValidateSnapshot(snapshot));
1964 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1965 CHECK(global);
1966 const v8::HeapGraphNode* obj1 =
1967 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1968 CHECK(obj1);
1969 const v8::HeapGraphNode* func;
1970 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get prop");
1971 CHECK(func);
1972 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set prop");
1973 CHECK(func);
1974 }
1975
1976
TEST(SlowCaseAccessors)1977 TEST(SlowCaseAccessors) {
1978 LocalContext env;
1979 v8::HandleScope scope(env->GetIsolate());
1980 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1981
1982 CompileRun("var obj1 = {};\n"
1983 "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};"
1984 "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1985 " return 42;\n"
1986 "});\n"
1987 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1988 " return this.value_ = value;\n"
1989 "});\n");
1990 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1991 CHECK(ValidateSnapshot(snapshot));
1992
1993 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1994 CHECK(global);
1995 const v8::HeapGraphNode* obj1 =
1996 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1997 CHECK(obj1);
1998 const v8::HeapGraphNode* func;
1999 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
2000 CHECK(func);
2001 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
2002 CHECK(!func);
2003 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
2004 CHECK(func);
2005 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
2006 CHECK(!func);
2007 }
2008
2009
TEST(HiddenPropertiesFastCase)2010 TEST(HiddenPropertiesFastCase) {
2011 v8::Isolate* isolate = CcTest::isolate();
2012 LocalContext env;
2013 v8::HandleScope scope(isolate);
2014 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2015
2016 CompileRun(
2017 "function C(x) { this.a = this; this.b = x; }\n"
2018 "c = new C(2012);\n");
2019 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2020 CHECK(ValidateSnapshot(snapshot));
2021 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2022 const v8::HeapGraphNode* c =
2023 GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
2024 CHECK(c);
2025 const v8::HeapGraphNode* hidden_props =
2026 GetProperty(c, v8::HeapGraphEdge::kProperty, "<symbol>");
2027 CHECK(!hidden_props);
2028
2029 v8::Local<v8::Value> cHandle =
2030 env->Global()->Get(env.local(), v8_str("c")).ToLocalChecked();
2031 CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
2032 cHandle->ToObject(env.local())
2033 .ToLocalChecked()
2034 ->SetPrivate(env.local(),
2035 v8::Private::ForApi(env->GetIsolate(), v8_str("key")),
2036 v8_str("val"))
2037 .FromJust();
2038
2039 snapshot = heap_profiler->TakeHeapSnapshot();
2040 CHECK(ValidateSnapshot(snapshot));
2041 global = GetGlobalObject(snapshot);
2042 c = GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
2043 CHECK(c);
2044 hidden_props = GetProperty(c, v8::HeapGraphEdge::kProperty, "<symbol>");
2045 CHECK(hidden_props);
2046 }
2047
2048
TEST(AccessorInfo)2049 TEST(AccessorInfo) {
2050 LocalContext env;
2051 v8::HandleScope scope(env->GetIsolate());
2052 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2053
2054 CompileRun("function foo(x) { }\n");
2055 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2056 CHECK(ValidateSnapshot(snapshot));
2057 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2058 const v8::HeapGraphNode* foo =
2059 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2060 CHECK(foo);
2061 const v8::HeapGraphNode* map =
2062 GetProperty(foo, v8::HeapGraphEdge::kInternal, "map");
2063 CHECK(map);
2064 const v8::HeapGraphNode* descriptors =
2065 GetProperty(map, v8::HeapGraphEdge::kInternal, "descriptors");
2066 CHECK(descriptors);
2067 const v8::HeapGraphNode* length_name =
2068 GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "2");
2069 CHECK(length_name);
2070 CHECK_EQ(0, strcmp("length", *v8::String::Utf8Value(length_name->GetName())));
2071 const v8::HeapGraphNode* length_accessor =
2072 GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "4");
2073 CHECK(length_accessor);
2074 CHECK_EQ(0, strcmp("system / ExecutableAccessorInfo",
2075 *v8::String::Utf8Value(length_accessor->GetName())));
2076 const v8::HeapGraphNode* name =
2077 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "name");
2078 CHECK(name);
2079 const v8::HeapGraphNode* getter =
2080 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "getter");
2081 CHECK(getter);
2082 const v8::HeapGraphNode* setter =
2083 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "setter");
2084 CHECK(setter);
2085 }
2086
2087
HasWeakEdge(const v8::HeapGraphNode * node)2088 bool HasWeakEdge(const v8::HeapGraphNode* node) {
2089 for (int i = 0; i < node->GetChildrenCount(); ++i) {
2090 const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
2091 if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
2092 }
2093 return false;
2094 }
2095
2096
HasWeakGlobalHandle()2097 bool HasWeakGlobalHandle() {
2098 v8::Isolate* isolate = CcTest::isolate();
2099 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2100 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2101 CHECK(ValidateSnapshot(snapshot));
2102 const v8::HeapGraphNode* gc_roots = GetNode(
2103 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2104 CHECK(gc_roots);
2105 const v8::HeapGraphNode* global_handles = GetNode(
2106 gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
2107 CHECK(global_handles);
2108 return HasWeakEdge(global_handles);
2109 }
2110
2111
PersistentHandleCallback(const v8::WeakCallbackInfo<v8::Persistent<v8::Object>> & data)2112 static void PersistentHandleCallback(
2113 const v8::WeakCallbackInfo<v8::Persistent<v8::Object> >& data) {
2114 data.GetParameter()->Reset();
2115 }
2116
2117
TEST(WeakGlobalHandle)2118 TEST(WeakGlobalHandle) {
2119 LocalContext env;
2120 v8::HandleScope scope(env->GetIsolate());
2121
2122 CHECK(!HasWeakGlobalHandle());
2123
2124 v8::Persistent<v8::Object> handle(env->GetIsolate(),
2125 v8::Object::New(env->GetIsolate()));
2126 handle.SetWeak(&handle, PersistentHandleCallback,
2127 v8::WeakCallbackType::kParameter);
2128
2129 CHECK(HasWeakGlobalHandle());
2130 }
2131
2132
TEST(SfiAndJsFunctionWeakRefs)2133 TEST(SfiAndJsFunctionWeakRefs) {
2134 LocalContext env;
2135 v8::HandleScope scope(env->GetIsolate());
2136 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2137
2138 CompileRun(
2139 "fun = (function (x) { return function () { return x + 1; } })(1);");
2140 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2141 CHECK(ValidateSnapshot(snapshot));
2142 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2143 CHECK(global);
2144 const v8::HeapGraphNode* fun =
2145 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
2146 CHECK(!HasWeakEdge(fun));
2147 const v8::HeapGraphNode* shared =
2148 GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
2149 CHECK(!HasWeakEdge(shared));
2150 }
2151
2152
TEST(NoDebugObjectInSnapshot)2153 TEST(NoDebugObjectInSnapshot) {
2154 LocalContext env;
2155 v8::HandleScope scope(env->GetIsolate());
2156 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2157
2158 CHECK(CcTest::i_isolate()->debug()->Load());
2159 CompileRun("foo = {};");
2160 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2161 CHECK(ValidateSnapshot(snapshot));
2162 const v8::HeapGraphNode* root = snapshot->GetRoot();
2163 int globals_count = 0;
2164 for (int i = 0; i < root->GetChildrenCount(); ++i) {
2165 const v8::HeapGraphEdge* edge = root->GetChild(i);
2166 if (edge->GetType() == v8::HeapGraphEdge::kShortcut) {
2167 ++globals_count;
2168 const v8::HeapGraphNode* global = edge->GetToNode();
2169 const v8::HeapGraphNode* foo =
2170 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2171 CHECK(foo);
2172 }
2173 }
2174 CHECK_EQ(1, globals_count);
2175 }
2176
2177
TEST(AllStrongGcRootsHaveNames)2178 TEST(AllStrongGcRootsHaveNames) {
2179 LocalContext env;
2180 v8::HandleScope scope(env->GetIsolate());
2181 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2182
2183 CompileRun("foo = {};");
2184 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2185 CHECK(ValidateSnapshot(snapshot));
2186 const v8::HeapGraphNode* gc_roots = GetNode(
2187 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2188 CHECK(gc_roots);
2189 const v8::HeapGraphNode* strong_roots = GetNode(
2190 gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
2191 CHECK(strong_roots);
2192 for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
2193 const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
2194 CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
2195 v8::String::Utf8Value name(edge->GetName());
2196 CHECK(isalpha(**name));
2197 }
2198 }
2199
2200
TEST(NoRefsToNonEssentialEntries)2201 TEST(NoRefsToNonEssentialEntries) {
2202 LocalContext env;
2203 v8::HandleScope scope(env->GetIsolate());
2204 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2205 CompileRun("global_object = {};\n");
2206 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2207 CHECK(ValidateSnapshot(snapshot));
2208 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2209 const v8::HeapGraphNode* global_object =
2210 GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object");
2211 CHECK(global_object);
2212 const v8::HeapGraphNode* properties =
2213 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties");
2214 CHECK(!properties);
2215 const v8::HeapGraphNode* elements =
2216 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements");
2217 CHECK(!elements);
2218 }
2219
2220
TEST(MapHasDescriptorsAndTransitions)2221 TEST(MapHasDescriptorsAndTransitions) {
2222 LocalContext env;
2223 v8::HandleScope scope(env->GetIsolate());
2224 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2225 CompileRun("obj = { a: 10 };\n");
2226 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2227 CHECK(ValidateSnapshot(snapshot));
2228 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2229 const v8::HeapGraphNode* global_object =
2230 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
2231 CHECK(global_object);
2232
2233 const v8::HeapGraphNode* map =
2234 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "map");
2235 CHECK(map);
2236 const v8::HeapGraphNode* own_descriptors = GetProperty(
2237 map, v8::HeapGraphEdge::kInternal, "descriptors");
2238 CHECK(own_descriptors);
2239 const v8::HeapGraphNode* own_transitions = GetProperty(
2240 map, v8::HeapGraphEdge::kInternal, "transitions");
2241 CHECK(!own_transitions);
2242 }
2243
2244
TEST(ManyLocalsInSharedContext)2245 TEST(ManyLocalsInSharedContext) {
2246 LocalContext env;
2247 v8::HandleScope scope(env->GetIsolate());
2248 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2249 int num_objects = 6000;
2250 CompileRun(
2251 "var n = 6000;"
2252 "var result = [];"
2253 "result.push('(function outer() {');"
2254 "for (var i = 0; i < n; i++) {"
2255 " var f = 'function f_' + i + '() { ';"
2256 " if (i > 0)"
2257 " f += 'f_' + (i - 1) + '();';"
2258 " f += ' }';"
2259 " result.push(f);"
2260 "}"
2261 "result.push('return f_' + (n - 1) + ';');"
2262 "result.push('})()');"
2263 "var ok = eval(result.join('\\n'));");
2264 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2265 CHECK(ValidateSnapshot(snapshot));
2266
2267 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2268 CHECK(global);
2269 const v8::HeapGraphNode* ok_object =
2270 GetProperty(global, v8::HeapGraphEdge::kProperty, "ok");
2271 CHECK(ok_object);
2272 const v8::HeapGraphNode* context_object =
2273 GetProperty(ok_object, v8::HeapGraphEdge::kInternal, "context");
2274 CHECK(context_object);
2275 // Check the objects are not duplicated in the context.
2276 CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1,
2277 context_object->GetChildrenCount());
2278 // Check all the objects have got their names.
2279 // ... well check just every 15th because otherwise it's too slow in debug.
2280 for (int i = 0; i < num_objects - 1; i += 15) {
2281 i::EmbeddedVector<char, 100> var_name;
2282 i::SNPrintF(var_name, "f_%d", i);
2283 const v8::HeapGraphNode* f_object = GetProperty(
2284 context_object, v8::HeapGraphEdge::kContextVariable, var_name.start());
2285 CHECK(f_object);
2286 }
2287 }
2288
2289
TEST(AllocationSitesAreVisible)2290 TEST(AllocationSitesAreVisible) {
2291 LocalContext env;
2292 v8::Isolate* isolate = env->GetIsolate();
2293 v8::HandleScope scope(isolate);
2294 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2295 CompileRun(
2296 "fun = function () { var a = [3, 2, 1]; return a; }\n"
2297 "fun();");
2298 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2299 CHECK(ValidateSnapshot(snapshot));
2300
2301 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2302 CHECK(global);
2303 const v8::HeapGraphNode* fun_code =
2304 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
2305 CHECK(fun_code);
2306 const v8::HeapGraphNode* literals =
2307 GetProperty(fun_code, v8::HeapGraphEdge::kInternal, "literals");
2308 CHECK(literals);
2309 CHECK_EQ(v8::HeapGraphNode::kArray, literals->GetType());
2310 CHECK_EQ(1, literals->GetChildrenCount());
2311
2312 // The first value in the literals array should be the boilerplate,
2313 // after an AllocationSite.
2314 const v8::HeapGraphEdge* prop = literals->GetChild(0);
2315 const v8::HeapGraphNode* allocation_site = prop->GetToNode();
2316 v8::String::Utf8Value name(allocation_site->GetName());
2317 CHECK_EQ(0, strcmp("system / AllocationSite", *name));
2318 const v8::HeapGraphNode* transition_info =
2319 GetProperty(allocation_site, v8::HeapGraphEdge::kInternal,
2320 "transition_info");
2321 CHECK(transition_info);
2322
2323 const v8::HeapGraphNode* elements =
2324 GetProperty(transition_info, v8::HeapGraphEdge::kInternal,
2325 "elements");
2326 CHECK(elements);
2327 CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType());
2328 CHECK_EQ(v8::internal::FixedArray::SizeFor(3),
2329 static_cast<int>(elements->GetShallowSize()));
2330
2331 v8::Local<v8::Value> array_val =
2332 heap_profiler->FindObjectById(transition_info->GetId());
2333 CHECK(array_val->IsArray());
2334 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(array_val);
2335 // Verify the array is "a" in the code above.
2336 CHECK_EQ(3u, array->Length());
2337 CHECK(v8::Integer::New(isolate, 3)
2338 ->Equals(env.local(),
2339 array->Get(env.local(), v8::Integer::New(isolate, 0))
2340 .ToLocalChecked())
2341 .FromJust());
2342 CHECK(v8::Integer::New(isolate, 2)
2343 ->Equals(env.local(),
2344 array->Get(env.local(), v8::Integer::New(isolate, 1))
2345 .ToLocalChecked())
2346 .FromJust());
2347 CHECK(v8::Integer::New(isolate, 1)
2348 ->Equals(env.local(),
2349 array->Get(env.local(), v8::Integer::New(isolate, 2))
2350 .ToLocalChecked())
2351 .FromJust());
2352 }
2353
2354
TEST(JSFunctionHasCodeLink)2355 TEST(JSFunctionHasCodeLink) {
2356 LocalContext env;
2357 v8::HandleScope scope(env->GetIsolate());
2358 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2359 CompileRun("function foo(x, y) { return x + y; }\n");
2360 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2361 CHECK(ValidateSnapshot(snapshot));
2362 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2363 const v8::HeapGraphNode* foo_func =
2364 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2365 CHECK(foo_func);
2366 const v8::HeapGraphNode* code =
2367 GetProperty(foo_func, v8::HeapGraphEdge::kInternal, "code");
2368 CHECK(code);
2369 }
2370
2371
GetNodeByPath(const v8::HeapSnapshot * snapshot,const char * path[],int depth)2372 static const v8::HeapGraphNode* GetNodeByPath(const v8::HeapSnapshot* snapshot,
2373 const char* path[],
2374 int depth) {
2375 const v8::HeapGraphNode* node = snapshot->GetRoot();
2376 for (int current_depth = 0; current_depth < depth; ++current_depth) {
2377 int i, count = node->GetChildrenCount();
2378 for (i = 0; i < count; ++i) {
2379 const v8::HeapGraphEdge* edge = node->GetChild(i);
2380 const v8::HeapGraphNode* to_node = edge->GetToNode();
2381 v8::String::Utf8Value edge_name(edge->GetName());
2382 v8::String::Utf8Value node_name(to_node->GetName());
2383 i::EmbeddedVector<char, 100> name;
2384 i::SNPrintF(name, "%s::%s", *edge_name, *node_name);
2385 if (strstr(name.start(), path[current_depth])) {
2386 node = to_node;
2387 break;
2388 }
2389 }
2390 if (i == count) return NULL;
2391 }
2392 return node;
2393 }
2394
2395
TEST(CheckCodeNames)2396 TEST(CheckCodeNames) {
2397 LocalContext env;
2398 v8::HandleScope scope(env->GetIsolate());
2399 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2400 CompileRun("var a = 1.1;");
2401 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2402 CHECK(ValidateSnapshot(snapshot));
2403
2404 const char* stub_path[] = {
2405 "::(GC roots)",
2406 "::(Strong roots)",
2407 "code_stubs::",
2408 "::(ArraySingleArgumentConstructorStub code)"
2409 };
2410 const v8::HeapGraphNode* node = GetNodeByPath(snapshot,
2411 stub_path, arraysize(stub_path));
2412 CHECK(node);
2413
2414 const char* builtin_path1[] = {"::(GC roots)", "::(Builtins)",
2415 "::(KeyedLoadIC_Megamorphic builtin)"};
2416 node = GetNodeByPath(snapshot, builtin_path1, arraysize(builtin_path1));
2417 CHECK(node);
2418
2419 const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)",
2420 "::(CompileLazy builtin)"};
2421 node = GetNodeByPath(snapshot, builtin_path2, arraysize(builtin_path2));
2422 CHECK(node);
2423 v8::String::Utf8Value node_name(node->GetName());
2424 CHECK_EQ(0, strcmp("(CompileLazy builtin)", *node_name));
2425 }
2426
2427
2428 static const char* record_trace_tree_source =
2429 "var topFunctions = [];\n"
2430 "var global = this;\n"
2431 "function generateFunctions(width, depth) {\n"
2432 " var script = [];\n"
2433 " for (var i = 0; i < width; i++) {\n"
2434 " for (var j = 0; j < depth; j++) {\n"
2435 " script.push('function f_' + i + '_' + j + '(x) {\\n');\n"
2436 " script.push(' try {\\n');\n"
2437 " if (j < depth-2) {\n"
2438 " script.push(' return f_' + i + '_' + (j+1) + '(x+1);\\n');\n"
2439 " } else if (j == depth - 2) {\n"
2440 " script.push(' return new f_' + i + '_' + (depth - 1) + '();\\n');\n"
2441 " } else if (j == depth - 1) {\n"
2442 " script.push(' this.ts = Date.now();\\n');\n"
2443 " }\n"
2444 " script.push(' } catch (e) {}\\n');\n"
2445 " script.push('}\\n');\n"
2446 " \n"
2447 " }\n"
2448 " }\n"
2449 " var script = script.join('');\n"
2450 " // throw script;\n"
2451 " global.eval(script);\n"
2452 " for (var i = 0; i < width; i++) {\n"
2453 " topFunctions.push(this['f_' + i + '_0']);\n"
2454 " }\n"
2455 "}\n"
2456 "\n"
2457 "var width = 3;\n"
2458 "var depth = 3;\n"
2459 "generateFunctions(width, depth);\n"
2460 "var instances = [];\n"
2461 "function start() {\n"
2462 " for (var i = 0; i < width; i++) {\n"
2463 " instances.push(topFunctions[i](0));\n"
2464 " }\n"
2465 "}\n"
2466 "\n"
2467 "for (var i = 0; i < 100; i++) start();\n";
2468
2469
FindNode(AllocationTracker * tracker,const Vector<const char * > & names)2470 static AllocationTraceNode* FindNode(
2471 AllocationTracker* tracker, const Vector<const char*>& names) {
2472 AllocationTraceNode* node = tracker->trace_tree()->root();
2473 for (int i = 0; node != NULL && i < names.length(); i++) {
2474 const char* name = names[i];
2475 Vector<AllocationTraceNode*> children = node->children();
2476 node = NULL;
2477 for (int j = 0; j < children.length(); j++) {
2478 unsigned index = children[j]->function_info_index();
2479 AllocationTracker::FunctionInfo* info =
2480 tracker->function_info_list()[index];
2481 if (info && strcmp(info->name, name) == 0) {
2482 node = children[j];
2483 break;
2484 }
2485 }
2486 }
2487 return node;
2488 }
2489
2490
TEST(ArrayGrowLeftTrim)2491 TEST(ArrayGrowLeftTrim) {
2492 LocalContext env;
2493 v8::HandleScope scope(env->GetIsolate());
2494 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2495 heap_profiler->StartTrackingHeapObjects(true);
2496
2497 CompileRun(
2498 "var a = [];\n"
2499 "for (var i = 0; i < 5; ++i)\n"
2500 " a[i] = i;\n"
2501 "for (var i = 0; i < 3; ++i)\n"
2502 " a.shift();\n");
2503
2504 const char* names[] = {""};
2505 AllocationTracker* tracker =
2506 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2507 CHECK(tracker);
2508 // Resolve all function locations.
2509 tracker->PrepareForSerialization();
2510 // Print for better diagnostics in case of failure.
2511 tracker->trace_tree()->Print(tracker);
2512
2513 AllocationTraceNode* node =
2514 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2515 CHECK(node);
2516 CHECK_GE(node->allocation_count(), 2u);
2517 CHECK_GE(node->allocation_size(), 4u * 5u);
2518 heap_profiler->StopTrackingHeapObjects();
2519 }
2520
2521
TEST(TrackHeapAllocations)2522 TEST(TrackHeapAllocations) {
2523 v8::HandleScope scope(v8::Isolate::GetCurrent());
2524 LocalContext env;
2525
2526 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2527 heap_profiler->StartTrackingHeapObjects(true);
2528
2529 CompileRun(record_trace_tree_source);
2530
2531 AllocationTracker* tracker =
2532 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2533 CHECK(tracker);
2534 // Resolve all function locations.
2535 tracker->PrepareForSerialization();
2536 // Print for better diagnostics in case of failure.
2537 tracker->trace_tree()->Print(tracker);
2538
2539 const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
2540 AllocationTraceNode* node =
2541 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2542 CHECK(node);
2543 CHECK_GE(node->allocation_count(), 100u);
2544 CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2545 heap_profiler->StopTrackingHeapObjects();
2546 }
2547
2548
2549 static const char* inline_heap_allocation_source =
2550 "function f_0(x) {\n"
2551 " return f_1(x+1);\n"
2552 "}\n"
2553 "%NeverOptimizeFunction(f_0);\n"
2554 "function f_1(x) {\n"
2555 " return new f_2(x+1);\n"
2556 "}\n"
2557 "%NeverOptimizeFunction(f_1);\n"
2558 "function f_2(x) {\n"
2559 " this.foo = x;\n"
2560 "}\n"
2561 "var instances = [];\n"
2562 "function start() {\n"
2563 " instances.push(f_0(0));\n"
2564 "}\n"
2565 "\n"
2566 "for (var i = 0; i < 100; i++) start();\n";
2567
2568
TEST(TrackBumpPointerAllocations)2569 TEST(TrackBumpPointerAllocations) {
2570 i::FLAG_allow_natives_syntax = true;
2571 v8::HandleScope scope(v8::Isolate::GetCurrent());
2572 LocalContext env;
2573
2574 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2575 const char* names[] = {"", "start", "f_0", "f_1"};
2576 // First check that normally all allocations are recorded.
2577 {
2578 heap_profiler->StartTrackingHeapObjects(true);
2579
2580 CompileRun(inline_heap_allocation_source);
2581
2582 AllocationTracker* tracker =
2583 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2584 CHECK(tracker);
2585 // Resolve all function locations.
2586 tracker->PrepareForSerialization();
2587 // Print for better diagnostics in case of failure.
2588 tracker->trace_tree()->Print(tracker);
2589
2590 AllocationTraceNode* node =
2591 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2592 CHECK(node);
2593 CHECK_GE(node->allocation_count(), 100u);
2594 CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2595 heap_profiler->StopTrackingHeapObjects();
2596 }
2597
2598 {
2599 heap_profiler->StartTrackingHeapObjects(true);
2600
2601 // Now check that not all allocations are tracked if we manually reenable
2602 // inline allocations.
2603 CHECK(CcTest::heap()->inline_allocation_disabled());
2604 CcTest::heap()->EnableInlineAllocation();
2605
2606 CompileRun(inline_heap_allocation_source);
2607
2608 AllocationTracker* tracker =
2609 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2610 CHECK(tracker);
2611 // Resolve all function locations.
2612 tracker->PrepareForSerialization();
2613 // Print for better diagnostics in case of failure.
2614 tracker->trace_tree()->Print(tracker);
2615
2616 AllocationTraceNode* node =
2617 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2618 CHECK(node);
2619 CHECK_LT(node->allocation_count(), 100u);
2620
2621 CcTest::heap()->DisableInlineAllocation();
2622 heap_profiler->StopTrackingHeapObjects();
2623 }
2624 }
2625
2626
TEST(TrackV8ApiAllocation)2627 TEST(TrackV8ApiAllocation) {
2628 v8::HandleScope scope(v8::Isolate::GetCurrent());
2629 LocalContext env;
2630
2631 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2632 const char* names[] = { "(V8 API)" };
2633 heap_profiler->StartTrackingHeapObjects(true);
2634
2635 v8::Local<v8::Object> o1 = v8::Object::New(env->GetIsolate());
2636 o1->Clone();
2637
2638 AllocationTracker* tracker =
2639 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2640 CHECK(tracker);
2641 // Resolve all function locations.
2642 tracker->PrepareForSerialization();
2643 // Print for better diagnostics in case of failure.
2644 tracker->trace_tree()->Print(tracker);
2645
2646 AllocationTraceNode* node =
2647 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2648 CHECK(node);
2649 CHECK_GE(node->allocation_count(), 2u);
2650 CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2651 heap_profiler->StopTrackingHeapObjects();
2652 }
2653
2654
TEST(ArrayBufferAndArrayBufferView)2655 TEST(ArrayBufferAndArrayBufferView) {
2656 LocalContext env;
2657 v8::HandleScope scope(env->GetIsolate());
2658 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2659 CompileRun("arr1 = new Uint32Array(100);\n");
2660 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2661 CHECK(ValidateSnapshot(snapshot));
2662 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2663 const v8::HeapGraphNode* arr1_obj =
2664 GetProperty(global, v8::HeapGraphEdge::kProperty, "arr1");
2665 CHECK(arr1_obj);
2666 const v8::HeapGraphNode* arr1_buffer =
2667 GetProperty(arr1_obj, v8::HeapGraphEdge::kInternal, "buffer");
2668 CHECK(arr1_buffer);
2669 const v8::HeapGraphNode* backing_store =
2670 GetProperty(arr1_buffer, v8::HeapGraphEdge::kInternal, "backing_store");
2671 CHECK(backing_store);
2672 CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize()));
2673 }
2674
2675
GetRetainersCount(const v8::HeapSnapshot * snapshot,const v8::HeapGraphNode * node)2676 static int GetRetainersCount(const v8::HeapSnapshot* snapshot,
2677 const v8::HeapGraphNode* node) {
2678 int count = 0;
2679 for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) {
2680 const v8::HeapGraphNode* parent = snapshot->GetNode(i);
2681 for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) {
2682 if (parent->GetChild(j)->GetToNode() == node) {
2683 ++count;
2684 }
2685 }
2686 }
2687 return count;
2688 }
2689
2690
TEST(ArrayBufferSharedBackingStore)2691 TEST(ArrayBufferSharedBackingStore) {
2692 LocalContext env;
2693 v8::Isolate* isolate = env->GetIsolate();
2694 v8::HandleScope handle_scope(isolate);
2695 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2696
2697 v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
2698 CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
2699 CHECK(!ab->IsExternal());
2700 v8::ArrayBuffer::Contents ab_contents = ab->Externalize();
2701 CHECK(ab->IsExternal());
2702
2703 CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
2704 void* data = ab_contents.Data();
2705 CHECK(data != NULL);
2706 v8::Local<v8::ArrayBuffer> ab2 =
2707 v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength());
2708 CHECK(ab2->IsExternal());
2709 env->Global()->Set(env.local(), v8_str("ab1"), ab).FromJust();
2710 env->Global()->Set(env.local(), v8_str("ab2"), ab2).FromJust();
2711
2712 v8::Local<v8::Value> result = CompileRun("ab2.byteLength");
2713 CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
2714
2715 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2716 CHECK(ValidateSnapshot(snapshot));
2717 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2718 const v8::HeapGraphNode* ab1_node =
2719 GetProperty(global, v8::HeapGraphEdge::kProperty, "ab1");
2720 CHECK(ab1_node);
2721 const v8::HeapGraphNode* ab1_data =
2722 GetProperty(ab1_node, v8::HeapGraphEdge::kInternal, "backing_store");
2723 CHECK(ab1_data);
2724 const v8::HeapGraphNode* ab2_node =
2725 GetProperty(global, v8::HeapGraphEdge::kProperty, "ab2");
2726 CHECK(ab2_node);
2727 const v8::HeapGraphNode* ab2_data =
2728 GetProperty(ab2_node, v8::HeapGraphEdge::kInternal, "backing_store");
2729 CHECK(ab2_data);
2730 CHECK_EQ(ab1_data, ab2_data);
2731 CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data));
2732 free(data);
2733 }
2734
2735
TEST(BoxObject)2736 TEST(BoxObject) {
2737 v8::Isolate* isolate = CcTest::isolate();
2738 v8::HandleScope scope(isolate);
2739 LocalContext env;
2740 v8::Local<v8::Object> global_proxy = env->Global();
2741 v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
2742
2743 i::Factory* factory = CcTest::i_isolate()->factory();
2744 i::Handle<i::String> string = factory->NewStringFromStaticChars("string");
2745 i::Handle<i::Object> box = factory->NewBox(string);
2746 global->Set(env.local(), 0, v8::ToApiHandle<v8::Object>(box)).FromJust();
2747
2748 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2749 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2750 CHECK(ValidateSnapshot(snapshot));
2751 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
2752 const v8::HeapGraphNode* box_node =
2753 GetProperty(global_node, v8::HeapGraphEdge::kElement, "0");
2754 CHECK(box_node);
2755 v8::String::Utf8Value box_node_name(box_node->GetName());
2756 CHECK_EQ(0, strcmp("system / Box", *box_node_name));
2757 const v8::HeapGraphNode* box_value =
2758 GetProperty(box_node, v8::HeapGraphEdge::kInternal, "value");
2759 CHECK(box_value);
2760 }
2761
2762
TEST(WeakContainers)2763 TEST(WeakContainers) {
2764 i::FLAG_allow_natives_syntax = true;
2765 LocalContext env;
2766 v8::HandleScope scope(env->GetIsolate());
2767 if (!CcTest::i_isolate()->use_crankshaft()) return;
2768 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2769 CompileRun(
2770 "function foo(a) { return a.x; }\n"
2771 "obj = {x : 123};\n"
2772 "foo(obj);\n"
2773 "foo(obj);\n"
2774 "%OptimizeFunctionOnNextCall(foo);\n"
2775 "foo(obj);\n");
2776 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2777 CHECK(ValidateSnapshot(snapshot));
2778 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2779 const v8::HeapGraphNode* obj =
2780 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
2781 CHECK(obj);
2782 const v8::HeapGraphNode* map =
2783 GetProperty(obj, v8::HeapGraphEdge::kInternal, "map");
2784 CHECK(map);
2785 const v8::HeapGraphNode* dependent_code =
2786 GetProperty(map, v8::HeapGraphEdge::kInternal, "dependent_code");
2787 if (!dependent_code) return;
2788 int count = dependent_code->GetChildrenCount();
2789 CHECK_NE(0, count);
2790 for (int i = 0; i < count; ++i) {
2791 const v8::HeapGraphEdge* prop = dependent_code->GetChild(i);
2792 CHECK_EQ(v8::HeapGraphEdge::kWeak, prop->GetType());
2793 }
2794 }
2795
2796
ToAddress(int n)2797 static inline i::Address ToAddress(int n) {
2798 return reinterpret_cast<i::Address>(n);
2799 }
2800
2801
TEST(AddressToTraceMap)2802 TEST(AddressToTraceMap) {
2803 i::AddressToTraceMap map;
2804
2805 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(150)));
2806
2807 // [0x100, 0x200) -> 1
2808 map.AddRange(ToAddress(0x100), 0x100, 1U);
2809 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x50)));
2810 CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x100)));
2811 CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x150)));
2812 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x100 + 0x100)));
2813 CHECK_EQ(1u, map.size());
2814
2815 // [0x100, 0x200) -> 1, [0x200, 0x300) -> 2
2816 map.AddRange(ToAddress(0x200), 0x100, 2U);
2817 CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x2a0)));
2818 CHECK_EQ(2u, map.size());
2819
2820 // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2
2821 map.AddRange(ToAddress(0x180), 0x100, 3U);
2822 CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
2823 CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
2824 CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
2825 CHECK_EQ(3u, map.size());
2826
2827 // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2,
2828 // [0x400, 0x500) -> 4
2829 map.AddRange(ToAddress(0x400), 0x100, 4U);
2830 CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
2831 CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
2832 CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
2833 CHECK_EQ(4u, map.GetTraceNodeId(ToAddress(0x450)));
2834 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x500)));
2835 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x350)));
2836 CHECK_EQ(4u, map.size());
2837
2838 // [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5
2839 map.AddRange(ToAddress(0x200), 0x400, 5U);
2840 CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
2841 CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x400)));
2842 CHECK_EQ(3u, map.size());
2843
2844 // [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5
2845 map.AddRange(ToAddress(0x180), 0x80, 6U);
2846 map.AddRange(ToAddress(0x180), 0x80, 7U);
2847 CHECK_EQ(7u, map.GetTraceNodeId(ToAddress(0x180)));
2848 CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
2849 CHECK_EQ(3u, map.size());
2850
2851 map.Clear();
2852 CHECK_EQ(0u, map.size());
2853 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x400)));
2854 }
2855