OLD | NEW |
1 // Copyright 2009 the V8 project authors. All rights reserved. | 1 // Copyright 2009 the V8 project authors. All rights reserved. |
2 // | 2 // |
3 // Tests for heap profiler | 3 // Tests for heap profiler |
4 | 4 |
5 #ifdef ENABLE_LOGGING_AND_PROFILING | 5 #ifdef ENABLE_LOGGING_AND_PROFILING |
6 | 6 |
7 #include "v8.h" | 7 #include "v8.h" |
8 #include "heap-profiler.h" | 8 #include "heap-profiler.h" |
9 #include "snapshot.h" | 9 #include "snapshot.h" |
10 #include "string-stream.h" | 10 #include "string-stream.h" |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 private: | 49 private: |
50 i::Handle<i::String> f_name_; | 50 i::Handle<i::String> f_name_; |
51 int f_count_; | 51 int f_count_; |
52 }; | 52 }; |
53 | 53 |
54 } // namespace | 54 } // namespace |
55 | 55 |
56 | 56 |
57 TEST(ConstructorProfile) { | 57 TEST(ConstructorProfile) { |
58 v8::HandleScope scope; | 58 v8::HandleScope scope; |
59 v8::Handle<v8::Context> env = v8::Context::New(); | 59 LocalContext env; |
60 env->Enter(); | |
61 | 60 |
62 CompileAndRunScript( | 61 CompileAndRunScript( |
63 "function F() {} // A constructor\n" | 62 "function F() {} // A constructor\n" |
64 "var f1 = new F();\n" | 63 "var f1 = new F();\n" |
65 "var f2 = new F();\n"); | 64 "var f2 = new F();\n"); |
66 | 65 |
67 ConstructorHeapProfileTestHelper cons_profile; | 66 ConstructorHeapProfileTestHelper cons_profile; |
68 i::AssertNoAllocation no_alloc; | 67 i::AssertNoAllocation no_alloc; |
69 i::HeapIterator iterator; | 68 i::HeapIterator iterator; |
70 for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) | 69 for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 value.DebugPrint(&stream); | 136 value.DebugPrint(&stream); |
138 V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n%s", | 137 V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n%s", |
139 expected_source, value_source, | 138 expected_source, value_source, |
140 *stream.ToCString()); | 139 *stream.ToCString()); |
141 } | 140 } |
142 } | 141 } |
143 | 142 |
144 | 143 |
145 TEST(ClustersCoarserSimple) { | 144 TEST(ClustersCoarserSimple) { |
146 v8::HandleScope scope; | 145 v8::HandleScope scope; |
147 v8::Handle<v8::Context> env = v8::Context::New(); | 146 LocalContext env; |
148 env->Enter(); | |
149 | 147 |
150 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); | 148 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); |
151 | 149 |
152 JSObjectsRetainerTree tree; | 150 JSObjectsRetainerTree tree; |
153 JSObjectsCluster function(i::Heap::function_class_symbol()); | 151 JSObjectsCluster function(i::Heap::function_class_symbol()); |
154 JSObjectsCluster a(*i::Factory::NewStringFromAscii(i::CStrVector("A"))); | 152 JSObjectsCluster a(*i::Factory::NewStringFromAscii(i::CStrVector("A"))); |
155 JSObjectsCluster b(*i::Factory::NewStringFromAscii(i::CStrVector("B"))); | 153 JSObjectsCluster b(*i::Factory::NewStringFromAscii(i::CStrVector("B"))); |
156 | 154 |
157 // o1 <- Function | 155 // o1 <- Function |
158 JSObjectsCluster o1 = | 156 JSObjectsCluster o1 = |
(...skipping 17 matching lines...) Expand all Loading... |
176 | 174 |
177 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); | 175 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); |
178 CHECK_EQ(coarser.GetCoarseEquivalent(o3), coarser.GetCoarseEquivalent(o4)); | 176 CHECK_EQ(coarser.GetCoarseEquivalent(o3), coarser.GetCoarseEquivalent(o4)); |
179 CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o3)); | 177 CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o3)); |
180 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o5)); | 178 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o5)); |
181 } | 179 } |
182 | 180 |
183 | 181 |
184 TEST(ClustersCoarserMultipleConstructors) { | 182 TEST(ClustersCoarserMultipleConstructors) { |
185 v8::HandleScope scope; | 183 v8::HandleScope scope; |
186 v8::Handle<v8::Context> env = v8::Context::New(); | 184 LocalContext env; |
187 env->Enter(); | |
188 | 185 |
189 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); | 186 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); |
190 | 187 |
191 JSObjectsRetainerTree tree; | 188 JSObjectsRetainerTree tree; |
192 JSObjectsCluster function(i::Heap::function_class_symbol()); | 189 JSObjectsCluster function(i::Heap::function_class_symbol()); |
193 | 190 |
194 // o1 <- Function | 191 // o1 <- Function |
195 JSObjectsCluster o1 = | 192 JSObjectsCluster o1 = |
196 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100, &function); | 193 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100, &function); |
197 // a1 <- Function | 194 // a1 <- Function |
198 JSObjectsCluster a1 = | 195 JSObjectsCluster a1 = |
199 AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x1000, &function); | 196 AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x1000, &function); |
200 // o2 <- Function | 197 // o2 <- Function |
201 JSObjectsCluster o2 = | 198 JSObjectsCluster o2 = |
202 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x200, &function); | 199 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x200, &function); |
203 // a2 <- Function | 200 // a2 <- Function |
204 JSObjectsCluster a2 = | 201 JSObjectsCluster a2 = |
205 AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x2000, &function); | 202 AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x2000, &function); |
206 | 203 |
207 ClustersCoarser coarser; | 204 ClustersCoarser coarser; |
208 coarser.Process(&tree); | 205 coarser.Process(&tree); |
209 | 206 |
210 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); | 207 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); |
211 CHECK_EQ(coarser.GetCoarseEquivalent(a1), coarser.GetCoarseEquivalent(a2)); | 208 CHECK_EQ(coarser.GetCoarseEquivalent(a1), coarser.GetCoarseEquivalent(a2)); |
212 } | 209 } |
213 | 210 |
214 | 211 |
215 TEST(ClustersCoarserPathsTraversal) { | 212 TEST(ClustersCoarserPathsTraversal) { |
216 v8::HandleScope scope; | 213 v8::HandleScope scope; |
217 v8::Handle<v8::Context> env = v8::Context::New(); | 214 LocalContext env; |
218 env->Enter(); | |
219 | 215 |
220 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); | 216 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); |
221 | 217 |
222 JSObjectsRetainerTree tree; | 218 JSObjectsRetainerTree tree; |
223 | 219 |
224 // On the following graph: | 220 // On the following graph: |
225 // | 221 // |
226 // p | 222 // p |
227 // <- o21 <- o11 <- | 223 // <- o21 <- o11 <- |
228 // q o | 224 // q o |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
260 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p)); | 256 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p)); |
261 CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); | 257 CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); |
262 CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); | 258 CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); |
263 CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(p)); | 259 CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(p)); |
264 CHECK_NE(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(p)); | 260 CHECK_NE(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(p)); |
265 } | 261 } |
266 | 262 |
267 | 263 |
268 TEST(ClustersCoarserSelf) { | 264 TEST(ClustersCoarserSelf) { |
269 v8::HandleScope scope; | 265 v8::HandleScope scope; |
270 v8::Handle<v8::Context> env = v8::Context::New(); | 266 LocalContext env; |
271 env->Enter(); | |
272 | 267 |
273 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); | 268 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); |
274 | 269 |
275 JSObjectsRetainerTree tree; | 270 JSObjectsRetainerTree tree; |
276 | 271 |
277 // On the following graph: | 272 // On the following graph: |
278 // | 273 // |
279 // p (self-referencing) | 274 // p (self-referencing) |
280 // <- o1 <- | 275 // <- o1 <- |
281 // q (self-referencing) o | 276 // q (self-referencing) o |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
355 i::StringStream stream_; | 350 i::StringStream stream_; |
356 i::SmartPointer<const char> stream_str_; | 351 i::SmartPointer<const char> stream_str_; |
357 i::List<const char*> lines_; | 352 i::List<const char*> lines_; |
358 }; | 353 }; |
359 | 354 |
360 } // namespace | 355 } // namespace |
361 | 356 |
362 | 357 |
363 TEST(RetainerProfile) { | 358 TEST(RetainerProfile) { |
364 v8::HandleScope scope; | 359 v8::HandleScope scope; |
365 v8::Handle<v8::Context> env = v8::Context::New(); | 360 LocalContext env; |
366 env->Enter(); | |
367 | 361 |
368 CompileAndRunScript( | 362 CompileAndRunScript( |
369 "function A() {}\n" | 363 "function A() {}\n" |
370 "function B(x) { this.x = x; }\n" | 364 "function B(x) { this.x = x; }\n" |
371 "function C(x) { this.x1 = x; this.x2 = x; }\n" | 365 "function C(x) { this.x1 = x; this.x2 = x; }\n" |
372 "var a = new A();\n" | 366 "var a = new A();\n" |
373 "var b1 = new B(a), b2 = new B(a);\n" | 367 "var b1 = new B(a), b2 = new B(a);\n" |
374 "var c = new C(a);"); | 368 "var c = new C(a);"); |
375 | 369 |
376 RetainerHeapProfile ret_profile; | 370 RetainerHeapProfile ret_profile; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
424 bool has_A2; | 418 bool has_A2; |
425 bool has_B2; | 419 bool has_B2; |
426 bool has_C2; | 420 bool has_C2; |
427 }; | 421 }; |
428 | 422 |
429 } // namespace | 423 } // namespace |
430 | 424 |
431 | 425 |
432 static const v8::HeapGraphNode* GetGlobalObject( | 426 static const v8::HeapGraphNode* GetGlobalObject( |
433 const v8::HeapSnapshot* snapshot) { | 427 const v8::HeapSnapshot* snapshot) { |
434 CHECK_EQ(1, snapshot->GetHead()->GetChildrenCount()); | 428 CHECK_EQ(1, snapshot->GetRoot()->GetChildrenCount()); |
435 return snapshot->GetHead()->GetChild(0)->GetToNode(); | 429 return snapshot->GetRoot()->GetChild(0)->GetToNode(); |
436 } | 430 } |
437 | 431 |
438 | 432 |
439 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node, | 433 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node, |
440 v8::HeapGraphEdge::Type type, | 434 v8::HeapGraphEdge::Type type, |
441 const char* name) { | 435 const char* name) { |
442 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { | 436 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { |
443 const v8::HeapGraphEdge* prop = node->GetChild(i); | 437 const v8::HeapGraphEdge* prop = node->GetChild(i); |
444 v8::String::AsciiValue prop_name(prop->GetName()); | 438 v8::String::AsciiValue prop_name(prop->GetName()); |
445 if (prop->GetType() == type && strcmp(name, *prop_name) == 0) | 439 if (prop->GetType() == type && strcmp(name, *prop_name) == 0) |
446 return prop->GetToNode(); | 440 return prop->GetToNode(); |
447 } | 441 } |
448 return NULL; | 442 return NULL; |
449 } | 443 } |
450 | 444 |
451 | 445 |
| 446 static bool IsNodeRetainedAs(const v8::HeapGraphNode* node, |
| 447 v8::HeapGraphEdge::Type type, |
| 448 const char* name) { |
| 449 for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) { |
| 450 const v8::HeapGraphEdge* prop = node->GetRetainer(i); |
| 451 v8::String::AsciiValue prop_name(prop->GetName()); |
| 452 if (prop->GetType() == type && strcmp(name, *prop_name) == 0) |
| 453 return true; |
| 454 } |
| 455 return false; |
| 456 } |
| 457 |
| 458 |
452 static bool HasString(const v8::HeapGraphNode* node, const char* contents) { | 459 static bool HasString(const v8::HeapGraphNode* node, const char* contents) { |
453 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { | 460 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { |
454 const v8::HeapGraphEdge* prop = node->GetChild(i); | 461 const v8::HeapGraphEdge* prop = node->GetChild(i); |
455 const v8::HeapGraphNode* node = prop->GetToNode(); | 462 const v8::HeapGraphNode* node = prop->GetToNode(); |
456 if (node->GetType() == v8::HeapGraphNode::STRING) { | 463 if (node->GetType() == v8::HeapGraphNode::STRING) { |
457 v8::String::AsciiValue node_name(node->GetName()); | 464 v8::String::AsciiValue node_name(node->GetName()); |
458 if (strcmp(contents, *node_name) == 0) return true; | 465 if (strcmp(contents, *node_name) == 0) return true; |
459 } | 466 } |
460 } | 467 } |
461 return false; | 468 return false; |
462 } | 469 } |
463 | 470 |
464 | 471 |
465 TEST(HeapSnapshot) { | 472 TEST(HeapSnapshot) { |
466 v8::HandleScope scope; | 473 v8::HandleScope scope; |
467 | |
468 v8::Handle<v8::String> token1 = v8::String::New("token1"); | 474 v8::Handle<v8::String> token1 = v8::String::New("token1"); |
469 v8::Handle<v8::Context> env1 = v8::Context::New(); | 475 LocalContext env1; |
470 env1->SetSecurityToken(token1); | 476 env1->SetSecurityToken(token1); |
471 env1->Enter(); | |
472 | 477 |
473 CompileAndRunScript( | 478 CompileAndRunScript( |
474 "function A1() {}\n" | 479 "function A1() {}\n" |
475 "function B1(x) { this.x = x; }\n" | 480 "function B1(x) { this.x = x; }\n" |
476 "function C1(x) { this.x1 = x; this.x2 = x; }\n" | 481 "function C1(x) { this.x1 = x; this.x2 = x; }\n" |
477 "var a1 = new A1();\n" | 482 "var a1 = new A1();\n" |
478 "var b1_1 = new B1(a1), b1_2 = new B1(a1);\n" | 483 "var b1_1 = new B1(a1), b1_2 = new B1(a1);\n" |
479 "var c1 = new C1(a1);"); | 484 "var c1 = new C1(a1);"); |
480 | 485 |
481 v8::Handle<v8::String> token2 = v8::String::New("token2"); | 486 v8::Handle<v8::String> token2 = v8::String::New("token2"); |
482 v8::Handle<v8::Context> env2 = v8::Context::New(); | 487 LocalContext env2; |
483 env2->SetSecurityToken(token2); | 488 env2->SetSecurityToken(token2); |
484 env2->Enter(); | |
485 | 489 |
486 CompileAndRunScript( | 490 CompileAndRunScript( |
487 "function A2() {}\n" | 491 "function A2() {}\n" |
488 "function B2(x) { return function() { return typeof x; }; }\n" | 492 "function B2(x) { return function() { return typeof x; }; }\n" |
489 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n" | 493 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n" |
490 "var a2 = new A2();\n" | 494 "var a2 = new A2();\n" |
491 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n" | 495 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n" |
492 "var c2 = new C2(a2);"); | 496 "var c2 = new C2(a2);"); |
493 const v8::HeapSnapshot* snapshot_env2 = | 497 const v8::HeapSnapshot* snapshot_env2 = |
494 v8::HeapProfiler::TakeSnapshot(v8::String::New("env2")); | 498 v8::HeapProfiler::TakeSnapshot(v8::String::New("env2")); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
562 CHECK(has_c2_x1_ref); | 566 CHECK(has_c2_x1_ref); |
563 CHECK(has_c2_x2_ref); | 567 CHECK(has_c2_x2_ref); |
564 CHECK(has_c2_1_ref); | 568 CHECK(has_c2_1_ref); |
565 CHECK(has_b2_1_x_ref); | 569 CHECK(has_b2_1_x_ref); |
566 CHECK(has_b2_2_x_ref); | 570 CHECK(has_b2_2_x_ref); |
567 } | 571 } |
568 | 572 |
569 | 573 |
570 TEST(HeapSnapshotCodeObjects) { | 574 TEST(HeapSnapshotCodeObjects) { |
571 v8::HandleScope scope; | 575 v8::HandleScope scope; |
572 v8::Handle<v8::Context> env = v8::Context::New(); | 576 LocalContext env; |
573 env->Enter(); | |
574 | 577 |
575 CompileAndRunScript( | 578 CompileAndRunScript( |
576 "function lazy(x) { return x - 1; }\n" | 579 "function lazy(x) { return x - 1; }\n" |
577 "function compiled(x) { return x + 1; }\n" | 580 "function compiled(x) { return x + 1; }\n" |
578 "compiled(1)"); | 581 "compiled(1)"); |
579 const v8::HeapSnapshot* snapshot = | 582 const v8::HeapSnapshot* snapshot = |
580 v8::HeapProfiler::TakeSnapshot(v8::String::New("code")); | 583 v8::HeapProfiler::TakeSnapshot(v8::String::New("code")); |
581 | 584 |
582 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); | 585 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); |
583 const v8::HeapGraphNode* compiled = | 586 const v8::HeapGraphNode* compiled = |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
618 if (HasString(node, "x")) { | 621 if (HasString(node, "x")) { |
619 lazy_references_x = true; | 622 lazy_references_x = true; |
620 break; | 623 break; |
621 } | 624 } |
622 } | 625 } |
623 } | 626 } |
624 CHECK(compiled_references_x); | 627 CHECK(compiled_references_x); |
625 CHECK(!lazy_references_x); | 628 CHECK(!lazy_references_x); |
626 } | 629 } |
627 | 630 |
| 631 |
| 632 TEST(HeapEntryIdsAndGC) { |
| 633 v8::HandleScope scope; |
| 634 LocalContext env; |
| 635 |
| 636 CompileAndRunScript( |
| 637 "function A() {}\n" |
| 638 "function B(x) { this.x = x; }\n" |
| 639 "var a = new A();\n" |
| 640 "var b = new B(a);"); |
| 641 const v8::HeapSnapshot* snapshot1 = |
| 642 v8::HeapProfiler::TakeSnapshot(v8::String::New("s1")); |
| 643 |
| 644 i::Heap::CollectAllGarbage(true); // Enforce compaction. |
| 645 |
| 646 const v8::HeapSnapshot* snapshot2 = |
| 647 v8::HeapProfiler::TakeSnapshot(v8::String::New("s2")); |
| 648 |
| 649 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1); |
| 650 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); |
| 651 CHECK_NE(0, global1->GetId()); |
| 652 CHECK_EQ(global1->GetId(), global2->GetId()); |
| 653 const v8::HeapGraphNode* A1 = |
| 654 GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "A"); |
| 655 const v8::HeapGraphNode* A2 = |
| 656 GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "A"); |
| 657 CHECK_NE(0, A1->GetId()); |
| 658 CHECK_EQ(A1->GetId(), A2->GetId()); |
| 659 const v8::HeapGraphNode* B1 = |
| 660 GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "B"); |
| 661 const v8::HeapGraphNode* B2 = |
| 662 GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "B"); |
| 663 CHECK_NE(0, B1->GetId()); |
| 664 CHECK_EQ(B1->GetId(), B2->GetId()); |
| 665 const v8::HeapGraphNode* a1 = |
| 666 GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "a"); |
| 667 const v8::HeapGraphNode* a2 = |
| 668 GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "a"); |
| 669 CHECK_NE(0, a1->GetId()); |
| 670 CHECK_EQ(a1->GetId(), a2->GetId()); |
| 671 const v8::HeapGraphNode* b1 = |
| 672 GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "b"); |
| 673 const v8::HeapGraphNode* b2 = |
| 674 GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "b"); |
| 675 CHECK_NE(0, b1->GetId()); |
| 676 CHECK_EQ(b1->GetId(), b2->GetId()); |
| 677 } |
| 678 |
| 679 |
| 680 TEST(HeapSnapshotsDiff) { |
| 681 v8::HandleScope scope; |
| 682 LocalContext env; |
| 683 |
| 684 CompileAndRunScript( |
| 685 "function A() {}\n" |
| 686 "function B(x) { this.x = x; }\n" |
| 687 "var a = new A();\n" |
| 688 "var b = new B(a);"); |
| 689 const v8::HeapSnapshot* snapshot1 = |
| 690 v8::HeapProfiler::TakeSnapshot(v8::String::New("s1")); |
| 691 |
| 692 CompileAndRunScript( |
| 693 "delete a;\n" |
| 694 "b.x = null;\n" |
| 695 "var a = new A();\n" |
| 696 "var b2 = new B(a);"); |
| 697 const v8::HeapSnapshot* snapshot2 = |
| 698 v8::HeapProfiler::TakeSnapshot(v8::String::New("s2")); |
| 699 |
| 700 const v8::HeapSnapshotsDiff* diff = snapshot1->CompareWith(snapshot2); |
| 701 |
| 702 // Verify additions: ensure that addition of A and B was detected. |
| 703 const v8::HeapGraphNode* additions_root = diff->GetAdditionsRoot(); |
| 704 bool found_A = false, found_B = false; |
| 705 uint64_t s1_A_id = 0; |
| 706 for (int i = 0, count = additions_root->GetChildrenCount(); i < count; ++i) { |
| 707 const v8::HeapGraphEdge* prop = additions_root->GetChild(i); |
| 708 const v8::HeapGraphNode* node = prop->GetToNode(); |
| 709 if (node->GetType() == v8::HeapGraphNode::OBJECT) { |
| 710 v8::String::AsciiValue node_name(node->GetName()); |
| 711 if (strcmp(*node_name, "A") == 0) { |
| 712 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::PROPERTY, "a")); |
| 713 CHECK(!found_A); |
| 714 found_A = true; |
| 715 s1_A_id = node->GetId(); |
| 716 } else if (strcmp(*node_name, "B") == 0) { |
| 717 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::PROPERTY, "b2")); |
| 718 CHECK(!found_B); |
| 719 found_B = true; |
| 720 } |
| 721 } |
| 722 } |
| 723 CHECK(found_A); |
| 724 CHECK(found_B); |
| 725 |
| 726 // Verify deletions: ensure that deletion of A was detected. |
| 727 const v8::HeapGraphNode* deletions_root = diff->GetDeletionsRoot(); |
| 728 bool found_A_del = false; |
| 729 uint64_t s2_A_id = 0; |
| 730 for (int i = 0, count = deletions_root->GetChildrenCount(); i < count; ++i) { |
| 731 const v8::HeapGraphEdge* prop = deletions_root->GetChild(i); |
| 732 const v8::HeapGraphNode* node = prop->GetToNode(); |
| 733 if (node->GetType() == v8::HeapGraphNode::OBJECT) { |
| 734 v8::String::AsciiValue node_name(node->GetName()); |
| 735 if (strcmp(*node_name, "A") == 0) { |
| 736 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::PROPERTY, "a")); |
| 737 CHECK(!found_A_del); |
| 738 found_A_del = true; |
| 739 s2_A_id = node->GetId(); |
| 740 } |
| 741 } |
| 742 } |
| 743 CHECK(found_A_del); |
| 744 CHECK_NE(0, s1_A_id); |
| 745 CHECK(s1_A_id != s2_A_id); |
| 746 } |
| 747 |
628 #endif // ENABLE_LOGGING_AND_PROFILING | 748 #endif // ENABLE_LOGGING_AND_PROFILING |
OLD | NEW |