| 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 |