test-heap-profiler.cc revision 9dcf7e2f83591d471e88bf7d230651900b8e424b
1// Copyright 2009 the V8 project authors. All rights reserved. 2// 3// Tests for heap profiler 4 5#ifdef ENABLE_LOGGING_AND_PROFILING 6 7#include "v8.h" 8#include "heap-profiler.h" 9#include "snapshot.h" 10#include "string-stream.h" 11#include "cctest.h" 12#include "zone-inl.h" 13#include "../include/v8-profiler.h" 14 15namespace i = v8::internal; 16using i::ClustersCoarser; 17using i::JSObjectsCluster; 18using i::JSObjectsRetainerTree; 19using i::JSObjectsClusterTree; 20using i::RetainerHeapProfile; 21 22 23static void CompileAndRunScript(const char *src) { 24 v8::Script::Compile(v8::String::New(src))->Run(); 25} 26 27 28namespace { 29 30class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile { 31 public: 32 ConstructorHeapProfileTestHelper() 33 : i::ConstructorHeapProfile(), 34 f_name_(i::Factory::NewStringFromAscii(i::CStrVector("F"))), 35 f_count_(0) { 36 } 37 38 void Call(const JSObjectsCluster& cluster, 39 const i::NumberAndSizeInfo& number_and_size) { 40 if (f_name_->Equals(cluster.constructor())) { 41 CHECK_EQ(f_count_, 0); 42 f_count_ = number_and_size.number(); 43 CHECK_GT(f_count_, 0); 44 } 45 } 46 47 int f_count() { return f_count_; } 48 49 private: 50 i::Handle<i::String> f_name_; 51 int f_count_; 52}; 53 54} // namespace 55 56 57TEST(ConstructorProfile) { 58 v8::HandleScope scope; 59 v8::Handle<v8::Context> env = v8::Context::New(); 60 env->Enter(); 61 62 CompileAndRunScript( 63 "function F() {} // A constructor\n" 64 "var f1 = new F();\n" 65 "var f2 = new F();\n"); 66 67 ConstructorHeapProfileTestHelper cons_profile; 68 i::AssertNoAllocation no_alloc; 69 i::HeapIterator iterator; 70 for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) 71 cons_profile.CollectStats(obj); 72 CHECK_EQ(0, cons_profile.f_count()); 73 cons_profile.PrintStats(); 74 CHECK_EQ(2, cons_profile.f_count()); 75} 76 77 78static JSObjectsCluster AddHeapObjectToTree(JSObjectsRetainerTree* tree, 79 i::String* constructor, 80 int instance, 81 JSObjectsCluster* ref1 = NULL, 82 JSObjectsCluster* ref2 = NULL, 83 JSObjectsCluster* ref3 = NULL) { 84 JSObjectsCluster o(constructor, reinterpret_cast<i::Object*>(instance)); 85 JSObjectsClusterTree* o_tree = new JSObjectsClusterTree(); 86 JSObjectsClusterTree::Locator o_loc; 87 if (ref1 != NULL) o_tree->Insert(*ref1, &o_loc); 88 if (ref2 != NULL) o_tree->Insert(*ref2, &o_loc); 89 if (ref3 != NULL) o_tree->Insert(*ref3, &o_loc); 90 JSObjectsRetainerTree::Locator loc; 91 tree->Insert(o, &loc); 92 loc.set_value(o_tree); 93 return o; 94} 95 96 97static void AddSelfReferenceToTree(JSObjectsRetainerTree* tree, 98 JSObjectsCluster* self_ref) { 99 JSObjectsRetainerTree::Locator loc; 100 CHECK(tree->Find(*self_ref, &loc)); 101 JSObjectsClusterTree::Locator o_loc; 102 CHECK_NE(NULL, loc.value()); 103 loc.value()->Insert(*self_ref, &o_loc); 104} 105 106 107static inline void CheckEqualsHelper(const char* file, int line, 108 const char* expected_source, 109 const JSObjectsCluster& expected, 110 const char* value_source, 111 const JSObjectsCluster& value) { 112 if (JSObjectsCluster::Compare(expected, value) != 0) { 113 i::HeapStringAllocator allocator; 114 i::StringStream stream(&allocator); 115 stream.Add("# Expected: "); 116 expected.DebugPrint(&stream); 117 stream.Add("\n# Found: "); 118 value.DebugPrint(&stream); 119 V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n%s", 120 expected_source, value_source, 121 *stream.ToCString()); 122 } 123} 124 125 126static inline void CheckNonEqualsHelper(const char* file, int line, 127 const char* expected_source, 128 const JSObjectsCluster& expected, 129 const char* value_source, 130 const JSObjectsCluster& value) { 131 if (JSObjectsCluster::Compare(expected, value) == 0) { 132 i::HeapStringAllocator allocator; 133 i::StringStream stream(&allocator); 134 stream.Add("# !Expected: "); 135 expected.DebugPrint(&stream); 136 stream.Add("\n# Found: "); 137 value.DebugPrint(&stream); 138 V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n%s", 139 expected_source, value_source, 140 *stream.ToCString()); 141 } 142} 143 144 145TEST(ClustersCoarserSimple) { 146 v8::HandleScope scope; 147 v8::Handle<v8::Context> env = v8::Context::New(); 148 env->Enter(); 149 150 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); 151 152 JSObjectsRetainerTree tree; 153 JSObjectsCluster function(i::Heap::function_class_symbol()); 154 JSObjectsCluster a(*i::Factory::NewStringFromAscii(i::CStrVector("A"))); 155 JSObjectsCluster b(*i::Factory::NewStringFromAscii(i::CStrVector("B"))); 156 157 // o1 <- Function 158 JSObjectsCluster o1 = 159 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100, &function); 160 // o2 <- Function 161 JSObjectsCluster o2 = 162 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x200, &function); 163 // o3 <- A, B 164 JSObjectsCluster o3 = 165 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &a, &b); 166 // o4 <- B, A 167 JSObjectsCluster o4 = 168 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x400, &b, &a); 169 // o5 <- A, B, Function 170 JSObjectsCluster o5 = 171 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x500, 172 &a, &b, &function); 173 174 ClustersCoarser coarser; 175 coarser.Process(&tree); 176 177 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); 178 CHECK_EQ(coarser.GetCoarseEquivalent(o3), coarser.GetCoarseEquivalent(o4)); 179 CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o3)); 180 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o5)); 181} 182 183 184TEST(ClustersCoarserMultipleConstructors) { 185 v8::HandleScope scope; 186 v8::Handle<v8::Context> env = v8::Context::New(); 187 env->Enter(); 188 189 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); 190 191 JSObjectsRetainerTree tree; 192 JSObjectsCluster function(i::Heap::function_class_symbol()); 193 194 // o1 <- Function 195 JSObjectsCluster o1 = 196 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100, &function); 197 // a1 <- Function 198 JSObjectsCluster a1 = 199 AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x1000, &function); 200 // o2 <- Function 201 JSObjectsCluster o2 = 202 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x200, &function); 203 // a2 <- Function 204 JSObjectsCluster a2 = 205 AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x2000, &function); 206 207 ClustersCoarser coarser; 208 coarser.Process(&tree); 209 210 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); 211 CHECK_EQ(coarser.GetCoarseEquivalent(a1), coarser.GetCoarseEquivalent(a2)); 212} 213 214 215TEST(ClustersCoarserPathsTraversal) { 216 v8::HandleScope scope; 217 v8::Handle<v8::Context> env = v8::Context::New(); 218 env->Enter(); 219 220 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); 221 222 JSObjectsRetainerTree tree; 223 224 // On the following graph: 225 // 226 // p 227 // <- o21 <- o11 <- 228 // q o 229 // <- o22 <- o12 <- 230 // r 231 // 232 // we expect that coarser will deduce equivalences: p ~ q ~ r, 233 // o21 ~ o22, and o11 ~ o12. 234 235 JSObjectsCluster o = 236 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100); 237 JSObjectsCluster o11 = 238 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x110, &o); 239 JSObjectsCluster o12 = 240 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x120, &o); 241 JSObjectsCluster o21 = 242 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x210, &o11); 243 JSObjectsCluster o22 = 244 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x220, &o12); 245 JSObjectsCluster p = 246 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &o21); 247 JSObjectsCluster q = 248 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x310, &o21, &o22); 249 JSObjectsCluster r = 250 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x320, &o22); 251 252 ClustersCoarser coarser; 253 coarser.Process(&tree); 254 255 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o)); 256 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o11)); 257 CHECK_EQ(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o12)); 258 CHECK_EQ(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(o22)); 259 CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o21)); 260 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p)); 261 CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); 262 CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); 263 CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(p)); 264 CHECK_NE(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(p)); 265} 266 267 268TEST(ClustersCoarserSelf) { 269 v8::HandleScope scope; 270 v8::Handle<v8::Context> env = v8::Context::New(); 271 env->Enter(); 272 273 i::ZoneScope zn_scope(i::DELETE_ON_EXIT); 274 275 JSObjectsRetainerTree tree; 276 277 // On the following graph: 278 // 279 // p (self-referencing) 280 // <- o1 <- 281 // q (self-referencing) o 282 // <- o2 <- 283 // r (self-referencing) 284 // 285 // we expect that coarser will deduce equivalences: p ~ q ~ r, o1 ~ o2; 286 287 JSObjectsCluster o = 288 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100); 289 JSObjectsCluster o1 = 290 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x110, &o); 291 JSObjectsCluster o2 = 292 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x120, &o); 293 JSObjectsCluster p = 294 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &o1); 295 AddSelfReferenceToTree(&tree, &p); 296 JSObjectsCluster q = 297 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x310, &o1, &o2); 298 AddSelfReferenceToTree(&tree, &q); 299 JSObjectsCluster r = 300 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x320, &o2); 301 AddSelfReferenceToTree(&tree, &r); 302 303 ClustersCoarser coarser; 304 coarser.Process(&tree); 305 306 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o)); 307 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o1)); 308 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); 309 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p)); 310 CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); 311 CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); 312 CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(p)); 313} 314 315 316namespace { 317 318class RetainerProfilePrinter : public RetainerHeapProfile::Printer { 319 public: 320 RetainerProfilePrinter() : stream_(&allocator_), lines_(100) {} 321 322 void PrintRetainers(const JSObjectsCluster& cluster, 323 const i::StringStream& retainers) { 324 cluster.Print(&stream_); 325 stream_.Add("%s", *(retainers.ToCString())); 326 stream_.Put('\0'); 327 } 328 329 const char* GetRetainers(const char* constructor) { 330 FillLines(); 331 const size_t cons_len = strlen(constructor); 332 for (int i = 0; i < lines_.length(); ++i) { 333 if (strncmp(constructor, lines_[i], cons_len) == 0 && 334 lines_[i][cons_len] == ',') { 335 return lines_[i] + cons_len + 1; 336 } 337 } 338 return NULL; 339 } 340 341 private: 342 void FillLines() { 343 if (lines_.length() > 0) return; 344 stream_.Put('\0'); 345 stream_str_ = stream_.ToCString(); 346 const char* pos = *stream_str_; 347 while (pos != NULL && *pos != '\0') { 348 lines_.Add(pos); 349 pos = strchr(pos, '\0'); 350 if (pos != NULL) ++pos; 351 } 352 } 353 354 i::HeapStringAllocator allocator_; 355 i::StringStream stream_; 356 i::SmartPointer<const char> stream_str_; 357 i::List<const char*> lines_; 358}; 359 360} // namespace 361 362 363TEST(RetainerProfile) { 364 v8::HandleScope scope; 365 v8::Handle<v8::Context> env = v8::Context::New(); 366 env->Enter(); 367 368 CompileAndRunScript( 369 "function A() {}\n" 370 "function B(x) { this.x = x; }\n" 371 "function C(x) { this.x1 = x; this.x2 = x; }\n" 372 "var a = new A();\n" 373 "var b1 = new B(a), b2 = new B(a);\n" 374 "var c = new C(a);"); 375 376 RetainerHeapProfile ret_profile; 377 i::AssertNoAllocation no_alloc; 378 i::HeapIterator iterator; 379 for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) 380 ret_profile.CollectStats(obj); 381 RetainerProfilePrinter printer; 382 ret_profile.DebugPrintStats(&printer); 383 const char* retainers_of_a = printer.GetRetainers("A"); 384 // The order of retainers is unspecified, so we check string length, and 385 // verify each retainer separately. 386 CHECK_EQ(i::StrLength("(global property);1,B;2,C;2"), 387 i::StrLength(retainers_of_a)); 388 CHECK(strstr(retainers_of_a, "(global property);1") != NULL); 389 CHECK(strstr(retainers_of_a, "B;2") != NULL); 390 CHECK(strstr(retainers_of_a, "C;2") != NULL); 391 CHECK_EQ("(global property);2", printer.GetRetainers("B")); 392 CHECK_EQ("(global property);1", printer.GetRetainers("C")); 393} 394 395 396namespace { 397 398class NamedEntriesDetector { 399 public: 400 NamedEntriesDetector() 401 : has_A1(false), has_B1(false), has_C1(false), 402 has_A2(false), has_B2(false), has_C2(false) { 403 } 404 405 void Apply(i::HeapEntry* entry) { 406 const char* node_name = entry->name(); 407 if (strcmp("A1", node_name) == 0 408 && entry->GetRetainingPaths()->length() > 0) has_A1 = true; 409 if (strcmp("B1", node_name) == 0 410 && entry->GetRetainingPaths()->length() > 0) has_B1 = true; 411 if (strcmp("C1", node_name) == 0 412 && entry->GetRetainingPaths()->length() > 0) has_C1 = true; 413 if (strcmp("A2", node_name) == 0 414 && entry->GetRetainingPaths()->length() > 0) has_A2 = true; 415 if (strcmp("B2", node_name) == 0 416 && entry->GetRetainingPaths()->length() > 0) has_B2 = true; 417 if (strcmp("C2", node_name) == 0 418 && entry->GetRetainingPaths()->length() > 0) has_C2 = true; 419 } 420 421 bool has_A1; 422 bool has_B1; 423 bool has_C1; 424 bool has_A2; 425 bool has_B2; 426 bool has_C2; 427}; 428 429} // namespace 430 431 432static const v8::HeapGraphNode* GetGlobalObject( 433 const v8::HeapSnapshot* snapshot) { 434 CHECK_EQ(1, snapshot->GetHead()->GetChildrenCount()); 435 return snapshot->GetHead()->GetChild(0)->GetToNode(); 436} 437 438 439static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node, 440 v8::HeapGraphEdge::Type type, 441 const char* name) { 442 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { 443 const v8::HeapGraphEdge* prop = node->GetChild(i); 444 v8::String::AsciiValue prop_name(prop->GetName()); 445 if (prop->GetType() == type && strcmp(name, *prop_name) == 0) 446 return prop->GetToNode(); 447 } 448 return NULL; 449} 450 451 452static bool HasString(const v8::HeapGraphNode* node, const char* contents) { 453 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { 454 const v8::HeapGraphEdge* prop = node->GetChild(i); 455 const v8::HeapGraphNode* node = prop->GetToNode(); 456 if (node->GetType() == v8::HeapGraphNode::STRING) { 457 v8::String::AsciiValue node_name(node->GetName()); 458 if (strcmp(contents, *node_name) == 0) return true; 459 } 460 } 461 return false; 462} 463 464 465TEST(HeapSnapshot) { 466 v8::HandleScope scope; 467 468 v8::Handle<v8::String> token1 = v8::String::New("token1"); 469 v8::Handle<v8::Context> env1 = v8::Context::New(); 470 env1->SetSecurityToken(token1); 471 env1->Enter(); 472 473 CompileAndRunScript( 474 "function A1() {}\n" 475 "function B1(x) { this.x = x; }\n" 476 "function C1(x) { this.x1 = x; this.x2 = x; }\n" 477 "var a1 = new A1();\n" 478 "var b1_1 = new B1(a1), b1_2 = new B1(a1);\n" 479 "var c1 = new C1(a1);"); 480 481 v8::Handle<v8::String> token2 = v8::String::New("token2"); 482 v8::Handle<v8::Context> env2 = v8::Context::New(); 483 env2->SetSecurityToken(token2); 484 env2->Enter(); 485 486 CompileAndRunScript( 487 "function A2() {}\n" 488 "function B2(x) { return function() { return typeof x; }; }\n" 489 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n" 490 "var a2 = new A2();\n" 491 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n" 492 "var c2 = new C2(a2);"); 493 const v8::HeapSnapshot* snapshot_env2 = 494 v8::HeapProfiler::TakeSnapshot(v8::String::New("env2")); 495 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2); 496 497 // Verify, that JS global object of env2 doesn't have '..1' 498 // properties, but has '..2' properties. 499 CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "a1")); 500 CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b1_1")); 501 CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b1_2")); 502 CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "c1")); 503 const v8::HeapGraphNode* a2_node = 504 GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "a2"); 505 CHECK_NE(NULL, a2_node); 506 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b2_1")); 507 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b2_2")); 508 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "c2")); 509 510 // Verify that anything related to '[ABC]1' is not reachable. 511 NamedEntriesDetector det; 512 i::HeapSnapshot* i_snapshot_env2 = 513 const_cast<i::HeapSnapshot*>( 514 reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2)); 515 i_snapshot_env2->IterateEntries(&det); 516 CHECK(!det.has_A1); 517 CHECK(!det.has_B1); 518 CHECK(!det.has_C1); 519 CHECK(det.has_A2); 520 CHECK(det.has_B2); 521 CHECK(det.has_C2); 522 523 // Verify 'a2' object retainers. They are: 524 // - (global object).a2 525 // - c2.x1, c2.x2, c2[1] 526 // - b2_1 and b2_2 closures: via 'x' variable 527 CHECK_EQ(6, a2_node->GetRetainingPathsCount()); 528 bool has_global_obj_a2_ref = false; 529 bool has_c2_x1_ref = false, has_c2_x2_ref = false, has_c2_1_ref = false; 530 bool has_b2_1_x_ref = false, has_b2_2_x_ref = false; 531 for (int i = 0; i < a2_node->GetRetainingPathsCount(); ++i) { 532 const v8::HeapGraphPath* path = a2_node->GetRetainingPath(i); 533 const int edges_count = path->GetEdgesCount(); 534 CHECK_GT(edges_count, 0); 535 const v8::HeapGraphEdge* last_edge = path->GetEdge(edges_count - 1); 536 v8::String::AsciiValue last_edge_name(last_edge->GetName()); 537 if (strcmp("a2", *last_edge_name) == 0 538 && last_edge->GetType() == v8::HeapGraphEdge::PROPERTY) { 539 has_global_obj_a2_ref = true; 540 continue; 541 } 542 CHECK_GT(edges_count, 1); 543 const v8::HeapGraphEdge* prev_edge = path->GetEdge(edges_count - 2); 544 v8::String::AsciiValue prev_edge_name(prev_edge->GetName()); 545 if (strcmp("x1", *last_edge_name) == 0 546 && last_edge->GetType() == v8::HeapGraphEdge::PROPERTY 547 && strcmp("c2", *prev_edge_name) == 0) has_c2_x1_ref = true; 548 if (strcmp("x2", *last_edge_name) == 0 549 && last_edge->GetType() == v8::HeapGraphEdge::PROPERTY 550 && strcmp("c2", *prev_edge_name) == 0) has_c2_x2_ref = true; 551 if (strcmp("1", *last_edge_name) == 0 552 && last_edge->GetType() == v8::HeapGraphEdge::ELEMENT 553 && strcmp("c2", *prev_edge_name) == 0) has_c2_1_ref = true; 554 if (strcmp("x", *last_edge_name) == 0 555 && last_edge->GetType() == v8::HeapGraphEdge::CONTEXT_VARIABLE 556 && strcmp("b2_1", *prev_edge_name) == 0) has_b2_1_x_ref = true; 557 if (strcmp("x", *last_edge_name) == 0 558 && last_edge->GetType() == v8::HeapGraphEdge::CONTEXT_VARIABLE 559 && strcmp("b2_2", *prev_edge_name) == 0) has_b2_2_x_ref = true; 560 } 561 CHECK(has_global_obj_a2_ref); 562 CHECK(has_c2_x1_ref); 563 CHECK(has_c2_x2_ref); 564 CHECK(has_c2_1_ref); 565 CHECK(has_b2_1_x_ref); 566 CHECK(has_b2_2_x_ref); 567} 568 569 570TEST(HeapSnapshotCodeObjects) { 571 v8::HandleScope scope; 572 v8::Handle<v8::Context> env = v8::Context::New(); 573 env->Enter(); 574 575 CompileAndRunScript( 576 "function lazy(x) { return x - 1; }\n" 577 "function compiled(x) { return x + 1; }\n" 578 "compiled(1)"); 579 const v8::HeapSnapshot* snapshot = 580 v8::HeapProfiler::TakeSnapshot(v8::String::New("code")); 581 582 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 583 const v8::HeapGraphNode* compiled = 584 GetProperty(global, v8::HeapGraphEdge::PROPERTY, "compiled"); 585 CHECK_NE(NULL, compiled); 586 CHECK_EQ(v8::HeapGraphNode::CLOSURE, compiled->GetType()); 587 const v8::HeapGraphNode* lazy = 588 GetProperty(global, v8::HeapGraphEdge::PROPERTY, "lazy"); 589 CHECK_NE(NULL, lazy); 590 CHECK_EQ(v8::HeapGraphNode::CLOSURE, lazy->GetType()); 591 592 // Find references to code. 593 const v8::HeapGraphNode* compiled_code = 594 GetProperty(compiled, v8::HeapGraphEdge::INTERNAL, "code"); 595 CHECK_NE(NULL, compiled_code); 596 const v8::HeapGraphNode* lazy_code = 597 GetProperty(lazy, v8::HeapGraphEdge::INTERNAL, "code"); 598 CHECK_NE(NULL, lazy_code); 599 600 // Verify that non-compiled code doesn't contain references to "x" 601 // literal, while compiled code does. 602 bool compiled_references_x = false, lazy_references_x = false; 603 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) { 604 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i); 605 const v8::HeapGraphNode* node = prop->GetToNode(); 606 if (node->GetType() == v8::HeapGraphNode::CODE) { 607 if (HasString(node, "x")) { 608 compiled_references_x = true; 609 break; 610 } 611 } 612 } 613 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) { 614 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i); 615 const v8::HeapGraphNode* node = prop->GetToNode(); 616 if (node->GetType() == v8::HeapGraphNode::CODE) { 617 if (HasString(node, "x")) { 618 lazy_references_x = true; 619 break; 620 } 621 } 622 } 623 CHECK(compiled_references_x); 624 CHECK(!lazy_references_x); 625} 626 627#endif // ENABLE_LOGGING_AND_PROFILING 628