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