| 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 415 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 426 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { | 426 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { |
| 427 const v8::HeapGraphEdge* prop = node->GetChild(i); | 427 const v8::HeapGraphEdge* prop = node->GetChild(i); |
| 428 v8::String::AsciiValue prop_name(prop->GetName()); | 428 v8::String::AsciiValue prop_name(prop->GetName()); |
| 429 if (prop->GetType() == type && strcmp(name, *prop_name) == 0) | 429 if (prop->GetType() == type && strcmp(name, *prop_name) == 0) |
| 430 return prop->GetToNode(); | 430 return prop->GetToNode(); |
| 431 } | 431 } |
| 432 return NULL; | 432 return NULL; |
| 433 } | 433 } |
| 434 | 434 |
| 435 | 435 |
| 436 static bool IsNodeRetainedAs(const v8::HeapGraphNode* node, | |
| 437 v8::HeapGraphEdge::Type type, | |
| 438 const char* name) { | |
| 439 for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) { | |
| 440 const v8::HeapGraphEdge* prop = node->GetRetainer(i); | |
| 441 v8::String::AsciiValue prop_name(prop->GetName()); | |
| 442 if (prop->GetType() == type && strcmp(name, *prop_name) == 0) | |
| 443 return true; | |
| 444 } | |
| 445 return false; | |
| 446 } | |
| 447 | |
| 448 | |
| 449 static bool HasString(const v8::HeapGraphNode* node, const char* contents) { | 436 static bool HasString(const v8::HeapGraphNode* node, const char* contents) { |
| 450 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { | 437 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { |
| 451 const v8::HeapGraphEdge* prop = node->GetChild(i); | 438 const v8::HeapGraphEdge* prop = node->GetChild(i); |
| 452 const v8::HeapGraphNode* node = prop->GetToNode(); | 439 const v8::HeapGraphNode* node = prop->GetToNode(); |
| 453 if (node->GetType() == v8::HeapGraphNode::kString) { | 440 if (node->GetType() == v8::HeapGraphNode::kString) { |
| 454 v8::String::AsciiValue node_name(node->GetName()); | 441 v8::String::AsciiValue node_name(node->GetName()); |
| 455 if (strcmp(contents, *node_name) == 0) return true; | 442 if (strcmp(contents, *node_name) == 0) return true; |
| 456 } | 443 } |
| 457 } | 444 } |
| 458 return false; | 445 return false; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 489 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1")); | 476 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1")); |
| 490 CHECK_NE( | 477 CHECK_NE( |
| 491 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2")); | 478 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2")); |
| 492 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2")); | 479 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2")); |
| 493 | 480 |
| 494 NamedEntriesDetector det; | 481 NamedEntriesDetector det; |
| 495 i_snapshot_env2->IterateEntries(&det); | 482 i_snapshot_env2->IterateEntries(&det); |
| 496 CHECK(det.has_A2); | 483 CHECK(det.has_A2); |
| 497 CHECK(det.has_B2); | 484 CHECK(det.has_B2); |
| 498 CHECK(det.has_C2); | 485 CHECK(det.has_C2); |
| 499 | |
| 500 /* | |
| 501 // Currently disabled. Too many retaining paths emerge, need to | |
| 502 // reduce the amount. | |
| 503 | |
| 504 // Verify 'a2' object retainers. They are: | |
| 505 // - (global object).a2 | |
| 506 // - c2.x1, c2.x2, c2[1] | |
| 507 // - b2_1 and b2_2 closures: via 'x' variable | |
| 508 CHECK_EQ(6, a2_node->GetRetainingPathsCount()); | |
| 509 bool has_global_obj_a2_ref = false; | |
| 510 bool has_c2_x1_ref = false, has_c2_x2_ref = false, has_c2_1_ref = false; | |
| 511 bool has_b2_1_x_ref = false, has_b2_2_x_ref = false; | |
| 512 for (int i = 0; i < a2_node->GetRetainingPathsCount(); ++i) { | |
| 513 const v8::HeapGraphPath* path = a2_node->GetRetainingPath(i); | |
| 514 const int edges_count = path->GetEdgesCount(); | |
| 515 CHECK_GT(edges_count, 0); | |
| 516 const v8::HeapGraphEdge* last_edge = path->GetEdge(edges_count - 1); | |
| 517 v8::String::AsciiValue last_edge_name(last_edge->GetName()); | |
| 518 if (strcmp("a2", *last_edge_name) == 0 | |
| 519 && last_edge->GetType() == v8::HeapGraphEdge::kProperty) { | |
| 520 has_global_obj_a2_ref = true; | |
| 521 continue; | |
| 522 } | |
| 523 CHECK_GT(edges_count, 1); | |
| 524 const v8::HeapGraphEdge* prev_edge = path->GetEdge(edges_count - 2); | |
| 525 v8::String::AsciiValue prev_edge_name(prev_edge->GetName()); | |
| 526 if (strcmp("x1", *last_edge_name) == 0 | |
| 527 && last_edge->GetType() == v8::HeapGraphEdge::kProperty | |
| 528 && strcmp("c2", *prev_edge_name) == 0) has_c2_x1_ref = true; | |
| 529 if (strcmp("x2", *last_edge_name) == 0 | |
| 530 && last_edge->GetType() == v8::HeapGraphEdge::kProperty | |
| 531 && strcmp("c2", *prev_edge_name) == 0) has_c2_x2_ref = true; | |
| 532 if (strcmp("1", *last_edge_name) == 0 | |
| 533 && last_edge->GetType() == v8::HeapGraphEdge::kElement | |
| 534 && strcmp("c2", *prev_edge_name) == 0) has_c2_1_ref = true; | |
| 535 if (strcmp("x", *last_edge_name) == 0 | |
| 536 && last_edge->GetType() == v8::HeapGraphEdge::kContextVariable | |
| 537 && strcmp("b2_1", *prev_edge_name) == 0) has_b2_1_x_ref = true; | |
| 538 if (strcmp("x", *last_edge_name) == 0 | |
| 539 && last_edge->GetType() == v8::HeapGraphEdge::kContextVariable | |
| 540 && strcmp("b2_2", *prev_edge_name) == 0) has_b2_2_x_ref = true; | |
| 541 } | |
| 542 CHECK(has_global_obj_a2_ref); | |
| 543 CHECK(has_c2_x1_ref); | |
| 544 CHECK(has_c2_x2_ref); | |
| 545 CHECK(has_c2_1_ref); | |
| 546 CHECK(has_b2_1_x_ref); | |
| 547 CHECK(has_b2_2_x_ref); | |
| 548 */ | |
| 549 } | 486 } |
| 550 | 487 |
| 551 | 488 |
| 552 TEST(HeapSnapshotObjectSizes) { | 489 TEST(HeapSnapshotObjectSizes) { |
| 553 v8::HandleScope scope; | 490 v8::HandleScope scope; |
| 554 LocalContext env; | 491 LocalContext env; |
| 555 | 492 |
| 556 // -a-> X1 --a | 493 // -a-> X1 --a |
| 557 // x -b-> X2 <-| | 494 // x -b-> X2 <-| |
| 558 CompileRun( | 495 CompileRun( |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 767 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b"); | 704 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b"); |
| 768 CHECK_NE(NULL, b1); | 705 CHECK_NE(NULL, b1); |
| 769 const v8::HeapGraphNode* b2 = | 706 const v8::HeapGraphNode* b2 = |
| 770 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b"); | 707 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b"); |
| 771 CHECK_NE(NULL, b2); | 708 CHECK_NE(NULL, b2); |
| 772 CHECK_NE_UINT64_T(0, b1->GetId()); | 709 CHECK_NE_UINT64_T(0, b1->GetId()); |
| 773 CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId()); | 710 CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId()); |
| 774 } | 711 } |
| 775 | 712 |
| 776 | 713 |
| 777 TEST(HeapSnapshotsDiff) { | |
| 778 v8::HandleScope scope; | |
| 779 LocalContext env; | |
| 780 | |
| 781 CompileRun( | |
| 782 "function A() {}\n" | |
| 783 "function B(x) { this.x = x; }\n" | |
| 784 "function A2(a) { for (var i = 0; i < a; ++i) this[i] = i; }\n" | |
| 785 "var a = new A();\n" | |
| 786 "var b = new B(a);"); | |
| 787 const v8::HeapSnapshot* snapshot1 = | |
| 788 v8::HeapProfiler::TakeSnapshot(v8::String::New("s1")); | |
| 789 | |
| 790 CompileRun( | |
| 791 "delete a;\n" | |
| 792 "b.x = null;\n" | |
| 793 "var a = new A2(20);\n" | |
| 794 "var b2 = new B(a);"); | |
| 795 const v8::HeapSnapshot* snapshot2 = | |
| 796 v8::HeapProfiler::TakeSnapshot(v8::String::New("s2")); | |
| 797 | |
| 798 const v8::HeapSnapshotsDiff* diff = snapshot1->CompareWith(snapshot2); | |
| 799 | |
| 800 // Verify additions: ensure that addition of A and B was detected. | |
| 801 const v8::HeapGraphNode* additions_root = diff->GetAdditionsRoot(); | |
| 802 bool found_A = false, found_B = false; | |
| 803 uint64_t s1_A_id = 0; | |
| 804 for (int i = 0, count = additions_root->GetChildrenCount(); i < count; ++i) { | |
| 805 const v8::HeapGraphEdge* prop = additions_root->GetChild(i); | |
| 806 const v8::HeapGraphNode* node = prop->GetToNode(); | |
| 807 if (node->GetType() == v8::HeapGraphNode::kObject) { | |
| 808 v8::String::AsciiValue node_name(node->GetName()); | |
| 809 if (strcmp(*node_name, "A2") == 0) { | |
| 810 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "a")); | |
| 811 CHECK(!found_A); | |
| 812 found_A = true; | |
| 813 s1_A_id = node->GetId(); | |
| 814 } else if (strcmp(*node_name, "B") == 0) { | |
| 815 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "b2")); | |
| 816 CHECK(!found_B); | |
| 817 found_B = true; | |
| 818 } | |
| 819 } | |
| 820 } | |
| 821 CHECK(found_A); | |
| 822 CHECK(found_B); | |
| 823 | |
| 824 // Verify deletions: ensure that deletion of A was detected. | |
| 825 const v8::HeapGraphNode* deletions_root = diff->GetDeletionsRoot(); | |
| 826 bool found_A_del = false; | |
| 827 uint64_t s2_A_id = 0; | |
| 828 for (int i = 0, count = deletions_root->GetChildrenCount(); i < count; ++i) { | |
| 829 const v8::HeapGraphEdge* prop = deletions_root->GetChild(i); | |
| 830 const v8::HeapGraphNode* node = prop->GetToNode(); | |
| 831 if (node->GetType() == v8::HeapGraphNode::kObject) { | |
| 832 v8::String::AsciiValue node_name(node->GetName()); | |
| 833 if (strcmp(*node_name, "A") == 0) { | |
| 834 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "a")); | |
| 835 CHECK(!found_A_del); | |
| 836 found_A_del = true; | |
| 837 s2_A_id = node->GetId(); | |
| 838 } | |
| 839 } | |
| 840 } | |
| 841 CHECK(found_A_del); | |
| 842 CHECK_NE_UINT64_T(0, s1_A_id); | |
| 843 CHECK(s1_A_id != s2_A_id); | |
| 844 } | |
| 845 | |
| 846 | |
| 847 TEST(HeapSnapshotRootPreservedAfterSorting) { | 714 TEST(HeapSnapshotRootPreservedAfterSorting) { |
| 848 v8::HandleScope scope; | 715 v8::HandleScope scope; |
| 849 LocalContext env; | 716 LocalContext env; |
| 850 const v8::HeapSnapshot* snapshot = | 717 const v8::HeapSnapshot* snapshot = |
| 851 v8::HeapProfiler::TakeSnapshot(v8::String::New("s")); | 718 v8::HeapProfiler::TakeSnapshot(v8::String::New("s")); |
| 852 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); | 719 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); |
| 853 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( | 720 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( |
| 854 snapshot))->GetSortedEntriesList(); | 721 snapshot))->GetSortedEntriesList(); |
| 855 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); | 722 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); |
| 856 CHECK_EQ(root1, root2); | 723 CHECK_EQ(root1, root2); |
| (...skipping 590 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1447 const_cast<v8::HeapSnapshot*>(s2)->Delete(); | 1314 const_cast<v8::HeapSnapshot*>(s2)->Delete(); |
| 1448 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount()); | 1315 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount()); |
| 1449 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid2)); | 1316 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid2)); |
| 1450 CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3)); | 1317 CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3)); |
| 1451 const_cast<v8::HeapSnapshot*>(s3)->Delete(); | 1318 const_cast<v8::HeapSnapshot*>(s3)->Delete(); |
| 1452 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount()); | 1319 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount()); |
| 1453 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid3)); | 1320 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid3)); |
| 1454 } | 1321 } |
| 1455 | 1322 |
| 1456 #endif // ENABLE_LOGGING_AND_PROFILING | 1323 #endif // ENABLE_LOGGING_AND_PROFILING |
| OLD | NEW |