1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // 3 // Tests for heap profiler 4 5 #include "v8.h" 6 7 #include "cctest.h" 8 #include "heap-profiler.h" 9 #include "snapshot.h" 10 #include "utils-inl.h" 11 #include "../include/v8-profiler.h" 12 13 namespace { 14 15 class NamedEntriesDetector { 16 public: 17 NamedEntriesDetector() 18 : has_A2(false), has_B2(false), has_C2(false) { 19 } 20 21 void CheckEntry(i::HeapEntry* entry) { 22 if (strcmp(entry->name(), "A2") == 0) has_A2 = true; 23 if (strcmp(entry->name(), "B2") == 0) has_B2 = true; 24 if (strcmp(entry->name(), "C2") == 0) has_C2 = true; 25 } 26 27 void CheckAllReachables(i::HeapEntry* root) { 28 i::List<i::HeapEntry*> list(10); 29 list.Add(root); 30 root->paint(); 31 CheckEntry(root); 32 while (!list.is_empty()) { 33 i::HeapEntry* entry = list.RemoveLast(); 34 i::Vector<i::HeapGraphEdge> children = entry->children(); 35 for (int i = 0; i < children.length(); ++i) { 36 if (children[i].type() == i::HeapGraphEdge::kShortcut) continue; 37 i::HeapEntry* child = children[i].to(); 38 if (!child->painted()) { 39 list.Add(child); 40 child->paint(); 41 CheckEntry(child); 42 } 43 } 44 } 45 } 46 47 bool has_A2; 48 bool has_B2; 49 bool has_C2; 50 }; 51 52 } // namespace 53 54 55 static const v8::HeapGraphNode* GetGlobalObject( 56 const v8::HeapSnapshot* snapshot) { 57 CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount()); 58 const v8::HeapGraphNode* global_obj = 59 snapshot->GetRoot()->GetChild(0)->GetToNode(); 60 CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>( 61 reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6)); 62 return global_obj; 63 } 64 65 66 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node, 67 v8::HeapGraphEdge::Type type, 68 const char* name) { 69 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { 70 const v8::HeapGraphEdge* prop = node->GetChild(i); 71 v8::String::AsciiValue prop_name(prop->GetName()); 72 if (prop->GetType() == type && strcmp(name, *prop_name) == 0) 73 return prop->GetToNode(); 74 } 75 return NULL; 76 } 77 78 79 static bool HasString(const v8::HeapGraphNode* node, const char* contents) { 80 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { 81 const v8::HeapGraphEdge* prop = node->GetChild(i); 82 const v8::HeapGraphNode* node = prop->GetToNode(); 83 if (node->GetType() == v8::HeapGraphNode::kString) { 84 v8::String::AsciiValue node_name(node->GetName()); 85 if (strcmp(contents, *node_name) == 0) return true; 86 } 87 } 88 return false; 89 } 90 91 92 TEST(HeapSnapshot) { 93 v8::HandleScope scope; 94 LocalContext env2; 95 96 CompileRun( 97 "function A2() {}\n" 98 "function B2(x) { return function() { return typeof x; }; }\n" 99 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n" 100 "var a2 = new A2();\n" 101 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n" 102 "var c2 = new C2(a2);"); 103 const v8::HeapSnapshot* snapshot_env2 = 104 v8::HeapProfiler::TakeSnapshot(v8_str("env2")); 105 i::HeapSnapshot* i_snapshot_env2 = 106 const_cast<i::HeapSnapshot*>( 107 reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2)); 108 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2); 109 110 // Verify, that JS global object of env2 has '..2' properties. 111 const v8::HeapGraphNode* a2_node = 112 GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "a2"); 113 CHECK_NE(NULL, a2_node); 114 CHECK_NE( 115 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1")); 116 CHECK_NE( 117 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2")); 118 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2")); 119 120 // Paint all nodes reachable from global object. 121 NamedEntriesDetector det; 122 i_snapshot_env2->ClearPaint(); 123 det.CheckAllReachables(const_cast<i::HeapEntry*>( 124 reinterpret_cast<const i::HeapEntry*>(global_env2))); 125 CHECK(det.has_A2); 126 CHECK(det.has_B2); 127 CHECK(det.has_C2); 128 } 129 130 131 TEST(HeapSnapshotObjectSizes) { 132 v8::HandleScope scope; 133 LocalContext env; 134 135 // -a-> X1 --a 136 // x -b-> X2 <-| 137 CompileRun( 138 "function X(a, b) { this.a = a; this.b = b; }\n" 139 "x = new X(new X(), new X());\n" 140 "(function() { x.a.a = x.b; })();"); 141 const v8::HeapSnapshot* snapshot = 142 v8::HeapProfiler::TakeSnapshot(v8_str("sizes")); 143 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 144 const v8::HeapGraphNode* x = 145 GetProperty(global, v8::HeapGraphEdge::kShortcut, "x"); 146 CHECK_NE(NULL, x); 147 const v8::HeapGraphNode* x1 = 148 GetProperty(x, v8::HeapGraphEdge::kProperty, "a"); 149 CHECK_NE(NULL, x1); 150 const v8::HeapGraphNode* x2 = 151 GetProperty(x, v8::HeapGraphEdge::kProperty, "b"); 152 CHECK_NE(NULL, x2); 153 154 // Test sizes. 155 CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize()); 156 CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize()); 157 CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize()); 158 } 159 160 161 TEST(BoundFunctionInSnapshot) { 162 v8::HandleScope scope; 163 LocalContext env; 164 CompileRun( 165 "function myFunction(a, b) { this.a = a; this.b = b; }\n" 166 "function AAAAA() {}\n" 167 "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n"); 168 const v8::HeapSnapshot* snapshot = 169 v8::HeapProfiler::TakeSnapshot(v8_str("sizes")); 170 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 171 const v8::HeapGraphNode* f = 172 GetProperty(global, v8::HeapGraphEdge::kShortcut, "boundFunction"); 173 CHECK(f); 174 CHECK_EQ(v8::String::New("native_bind"), f->GetName()); 175 const v8::HeapGraphNode* bindings = 176 GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings"); 177 CHECK_NE(NULL, bindings); 178 CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType()); 179 CHECK_EQ(4, bindings->GetChildrenCount()); 180 181 const v8::HeapGraphNode* bound_this = GetProperty( 182 f, v8::HeapGraphEdge::kShortcut, "bound_this"); 183 CHECK(bound_this); 184 CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType()); 185 186 const v8::HeapGraphNode* bound_function = GetProperty( 187 f, v8::HeapGraphEdge::kShortcut, "bound_function"); 188 CHECK(bound_function); 189 CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType()); 190 191 const v8::HeapGraphNode* bound_argument = GetProperty( 192 f, v8::HeapGraphEdge::kShortcut, "bound_argument_1"); 193 CHECK(bound_argument); 194 CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType()); 195 } 196 197 198 TEST(HeapSnapshotEntryChildren) { 199 v8::HandleScope scope; 200 LocalContext env; 201 202 CompileRun( 203 "function A() { }\n" 204 "a = new A;"); 205 const v8::HeapSnapshot* snapshot = 206 v8::HeapProfiler::TakeSnapshot(v8_str("children")); 207 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 208 for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) { 209 const v8::HeapGraphEdge* prop = global->GetChild(i); 210 CHECK_EQ(global, prop->GetFromNode()); 211 } 212 const v8::HeapGraphNode* a = 213 GetProperty(global, v8::HeapGraphEdge::kProperty, "a"); 214 CHECK_NE(NULL, a); 215 for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) { 216 const v8::HeapGraphEdge* prop = a->GetChild(i); 217 CHECK_EQ(a, prop->GetFromNode()); 218 } 219 } 220 221 222 TEST(HeapSnapshotCodeObjects) { 223 v8::HandleScope scope; 224 LocalContext env; 225 226 CompileRun( 227 "function lazy(x) { return x - 1; }\n" 228 "function compiled(x) { return x + 1; }\n" 229 "var anonymous = (function() { return function() { return 0; } })();\n" 230 "compiled(1)"); 231 const v8::HeapSnapshot* snapshot = 232 v8::HeapProfiler::TakeSnapshot(v8_str("code")); 233 234 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 235 const v8::HeapGraphNode* compiled = 236 GetProperty(global, v8::HeapGraphEdge::kShortcut, "compiled"); 237 CHECK_NE(NULL, compiled); 238 CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType()); 239 const v8::HeapGraphNode* lazy = 240 GetProperty(global, v8::HeapGraphEdge::kShortcut, "lazy"); 241 CHECK_NE(NULL, lazy); 242 CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType()); 243 const v8::HeapGraphNode* anonymous = 244 GetProperty(global, v8::HeapGraphEdge::kShortcut, "anonymous"); 245 CHECK_NE(NULL, anonymous); 246 CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType()); 247 v8::String::AsciiValue anonymous_name(anonymous->GetName()); 248 CHECK_EQ("", *anonymous_name); 249 250 // Find references to code. 251 const v8::HeapGraphNode* compiled_code = 252 GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared"); 253 CHECK_NE(NULL, compiled_code); 254 const v8::HeapGraphNode* lazy_code = 255 GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared"); 256 CHECK_NE(NULL, lazy_code); 257 258 // Verify that non-compiled code doesn't contain references to "x" 259 // literal, while compiled code does. The scope info is stored in FixedArray 260 // objects attached to the SharedFunctionInfo. 261 bool compiled_references_x = false, lazy_references_x = false; 262 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) { 263 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i); 264 const v8::HeapGraphNode* node = prop->GetToNode(); 265 if (node->GetType() == v8::HeapGraphNode::kArray) { 266 if (HasString(node, "x")) { 267 compiled_references_x = true; 268 break; 269 } 270 } 271 } 272 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) { 273 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i); 274 const v8::HeapGraphNode* node = prop->GetToNode(); 275 if (node->GetType() == v8::HeapGraphNode::kArray) { 276 if (HasString(node, "x")) { 277 lazy_references_x = true; 278 break; 279 } 280 } 281 } 282 CHECK(compiled_references_x); 283 CHECK(!lazy_references_x); 284 } 285 286 287 TEST(HeapSnapshotHeapNumbers) { 288 v8::HandleScope scope; 289 LocalContext env; 290 CompileRun( 291 "a = 1; // a is Smi\n" 292 "b = 2.5; // b is HeapNumber"); 293 const v8::HeapSnapshot* snapshot = 294 v8::HeapProfiler::TakeSnapshot(v8_str("numbers")); 295 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 296 CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kShortcut, "a")); 297 const v8::HeapGraphNode* b = 298 GetProperty(global, v8::HeapGraphEdge::kShortcut, "b"); 299 CHECK_NE(NULL, b); 300 CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType()); 301 } 302 303 TEST(HeapSnapshotSlicedString) { 304 v8::HandleScope scope; 305 LocalContext env; 306 CompileRun( 307 "parent_string = \"123456789.123456789.123456789.123456789.123456789." 308 "123456789.123456789.123456789.123456789.123456789." 309 "123456789.123456789.123456789.123456789.123456789." 310 "123456789.123456789.123456789.123456789.123456789.\";" 311 "child_string = parent_string.slice(100);"); 312 const v8::HeapSnapshot* snapshot = 313 v8::HeapProfiler::TakeSnapshot(v8_str("strings")); 314 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 315 const v8::HeapGraphNode* parent_string = 316 GetProperty(global, v8::HeapGraphEdge::kShortcut, "parent_string"); 317 CHECK_NE(NULL, parent_string); 318 const v8::HeapGraphNode* child_string = 319 GetProperty(global, v8::HeapGraphEdge::kShortcut, "child_string"); 320 CHECK_NE(NULL, child_string); 321 const v8::HeapGraphNode* parent = 322 GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent"); 323 CHECK_EQ(parent_string, parent); 324 } 325 326 TEST(HeapSnapshotInternalReferences) { 327 v8::HandleScope scope; 328 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 329 global_template->SetInternalFieldCount(2); 330 LocalContext env(NULL, global_template); 331 v8::Handle<v8::Object> global_proxy = env->Global(); 332 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 333 CHECK_EQ(2, global->InternalFieldCount()); 334 v8::Local<v8::Object> obj = v8::Object::New(); 335 global->SetInternalField(0, v8_num(17)); 336 global->SetInternalField(1, obj); 337 const v8::HeapSnapshot* snapshot = 338 v8::HeapProfiler::TakeSnapshot(v8_str("internals")); 339 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot); 340 // The first reference will not present, because it's a Smi. 341 CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0")); 342 // The second reference is to an object. 343 CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1")); 344 } 345 346 347 // Trying to introduce a check helper for uint64_t causes many 348 // overloading ambiguities, so it seems easier just to cast 349 // them to a signed type. 350 #define CHECK_EQ_UINT64_T(a, b) \ 351 CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b)) 352 #define CHECK_NE_UINT64_T(a, b) \ 353 CHECK((a) != (b)) // NOLINT 354 355 TEST(HeapEntryIdsAndArrayShift) { 356 v8::HandleScope scope; 357 LocalContext env; 358 359 CompileRun( 360 "function AnObject() {\n" 361 " this.first = 'first';\n" 362 " this.second = 'second';\n" 363 "}\n" 364 "var a = new Array();\n" 365 "for (var i = 0; i < 10; ++i)\n" 366 " a.push(new AnObject());\n"); 367 const v8::HeapSnapshot* snapshot1 = 368 v8::HeapProfiler::TakeSnapshot(v8_str("s1")); 369 370 CompileRun( 371 "for (var i = 0; i < 1; ++i)\n" 372 " a.shift();\n"); 373 374 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 375 376 const v8::HeapSnapshot* snapshot2 = 377 v8::HeapProfiler::TakeSnapshot(v8_str("s2")); 378 379 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1); 380 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); 381 CHECK_NE_UINT64_T(0, global1->GetId()); 382 CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId()); 383 384 const v8::HeapGraphNode* a1 = 385 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a"); 386 CHECK_NE(NULL, a1); 387 const v8::HeapGraphNode* e1 = 388 GetProperty(a1, v8::HeapGraphEdge::kHidden, "1"); 389 CHECK_NE(NULL, e1); 390 const v8::HeapGraphNode* k1 = 391 GetProperty(e1, v8::HeapGraphEdge::kInternal, "elements"); 392 CHECK_NE(NULL, k1); 393 const v8::HeapGraphNode* a2 = 394 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a"); 395 CHECK_NE(NULL, a2); 396 const v8::HeapGraphNode* e2 = 397 GetProperty(a2, v8::HeapGraphEdge::kHidden, "1"); 398 CHECK_NE(NULL, e2); 399 const v8::HeapGraphNode* k2 = 400 GetProperty(e2, v8::HeapGraphEdge::kInternal, "elements"); 401 CHECK_NE(NULL, k2); 402 403 CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId()); 404 CHECK_EQ_UINT64_T(e1->GetId(), e2->GetId()); 405 CHECK_EQ_UINT64_T(k1->GetId(), k2->GetId()); 406 } 407 408 TEST(HeapEntryIdsAndGC) { 409 v8::HandleScope scope; 410 LocalContext env; 411 412 CompileRun( 413 "function A() {}\n" 414 "function B(x) { this.x = x; }\n" 415 "var a = new A();\n" 416 "var b = new B(a);"); 417 const v8::HeapSnapshot* snapshot1 = 418 v8::HeapProfiler::TakeSnapshot(v8_str("s1")); 419 420 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 421 422 const v8::HeapSnapshot* snapshot2 = 423 v8::HeapProfiler::TakeSnapshot(v8_str("s2")); 424 425 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1); 426 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); 427 CHECK_NE_UINT64_T(0, global1->GetId()); 428 CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId()); 429 const v8::HeapGraphNode* A1 = 430 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A"); 431 CHECK_NE(NULL, A1); 432 const v8::HeapGraphNode* A2 = 433 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A"); 434 CHECK_NE(NULL, A2); 435 CHECK_NE_UINT64_T(0, A1->GetId()); 436 CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId()); 437 const v8::HeapGraphNode* B1 = 438 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B"); 439 CHECK_NE(NULL, B1); 440 const v8::HeapGraphNode* B2 = 441 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B"); 442 CHECK_NE(NULL, B2); 443 CHECK_NE_UINT64_T(0, B1->GetId()); 444 CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId()); 445 const v8::HeapGraphNode* a1 = 446 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a"); 447 CHECK_NE(NULL, a1); 448 const v8::HeapGraphNode* a2 = 449 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a"); 450 CHECK_NE(NULL, a2); 451 CHECK_NE_UINT64_T(0, a1->GetId()); 452 CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId()); 453 const v8::HeapGraphNode* b1 = 454 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b"); 455 CHECK_NE(NULL, b1); 456 const v8::HeapGraphNode* b2 = 457 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b"); 458 CHECK_NE(NULL, b2); 459 CHECK_NE_UINT64_T(0, b1->GetId()); 460 CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId()); 461 } 462 463 464 TEST(HeapSnapshotRootPreservedAfterSorting) { 465 v8::HandleScope scope; 466 LocalContext env; 467 const v8::HeapSnapshot* snapshot = 468 v8::HeapProfiler::TakeSnapshot(v8_str("s")); 469 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); 470 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( 471 snapshot))->GetSortedEntriesList(); 472 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); 473 CHECK_EQ(root1, root2); 474 } 475 476 477 TEST(HeapEntryDominator) { 478 // The graph looks like this: 479 // 480 // -> node1 481 // a |^ 482 // -> node5 ba 483 // a v| 484 // node6 -> node2 485 // b a |^ 486 // -> node4 ba 487 // b v| 488 // -> node3 489 // 490 // The dominator for all nodes is node6. 491 492 v8::HandleScope scope; 493 LocalContext env; 494 495 CompileRun( 496 "function X(a, b) { this.a = a; this.b = b; }\n" 497 "node6 = new X(new X(new X()), new X(new X(),new X()));\n" 498 "(function(){\n" 499 "node6.a.a.b = node6.b.a; // node1 -> node2\n" 500 "node6.b.a.a = node6.a.a; // node2 -> node1\n" 501 "node6.b.a.b = node6.b.b; // node2 -> node3\n" 502 "node6.b.b.a = node6.b.a; // node3 -> node2\n" 503 "})();"); 504 505 const v8::HeapSnapshot* snapshot = 506 v8::HeapProfiler::TakeSnapshot(v8_str("dominators")); 507 508 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 509 CHECK_NE(NULL, global); 510 const v8::HeapGraphNode* node6 = 511 GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6"); 512 CHECK_NE(NULL, node6); 513 const v8::HeapGraphNode* node5 = 514 GetProperty(node6, v8::HeapGraphEdge::kProperty, "a"); 515 CHECK_NE(NULL, node5); 516 const v8::HeapGraphNode* node4 = 517 GetProperty(node6, v8::HeapGraphEdge::kProperty, "b"); 518 CHECK_NE(NULL, node4); 519 const v8::HeapGraphNode* node3 = 520 GetProperty(node4, v8::HeapGraphEdge::kProperty, "b"); 521 CHECK_NE(NULL, node3); 522 const v8::HeapGraphNode* node2 = 523 GetProperty(node4, v8::HeapGraphEdge::kProperty, "a"); 524 CHECK_NE(NULL, node2); 525 const v8::HeapGraphNode* node1 = 526 GetProperty(node5, v8::HeapGraphEdge::kProperty, "a"); 527 CHECK_NE(NULL, node1); 528 529 CHECK_EQ(node6, node1->GetDominatorNode()); 530 CHECK_EQ(node6, node2->GetDominatorNode()); 531 CHECK_EQ(node6, node3->GetDominatorNode()); 532 CHECK_EQ(node6, node4->GetDominatorNode()); 533 CHECK_EQ(node6, node5->GetDominatorNode()); 534 } 535 536 537 namespace { 538 539 class TestJSONStream : public v8::OutputStream { 540 public: 541 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {} 542 explicit TestJSONStream(int abort_countdown) 543 : eos_signaled_(0), abort_countdown_(abort_countdown) {} 544 virtual ~TestJSONStream() {} 545 virtual void EndOfStream() { ++eos_signaled_; } 546 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) { 547 if (abort_countdown_ > 0) --abort_countdown_; 548 if (abort_countdown_ == 0) return kAbort; 549 CHECK_GT(chars_written, 0); 550 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0'); 551 memcpy(chunk.start(), buffer, chars_written); 552 return kContinue; 553 } 554 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); } 555 int eos_signaled() { return eos_signaled_; } 556 int size() { return buffer_.size(); } 557 private: 558 i::Collector<char> buffer_; 559 int eos_signaled_; 560 int abort_countdown_; 561 }; 562 563 class AsciiResource: public v8::String::ExternalAsciiStringResource { 564 public: 565 explicit AsciiResource(i::Vector<char> string): data_(string.start()) { 566 length_ = string.length(); 567 } 568 virtual const char* data() const { return data_; } 569 virtual size_t length() const { return length_; } 570 private: 571 const char* data_; 572 size_t length_; 573 }; 574 575 } // namespace 576 577 TEST(HeapSnapshotJSONSerialization) { 578 v8::HandleScope scope; 579 LocalContext env; 580 581 #define STRING_LITERAL_FOR_TEST \ 582 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\"" 583 CompileRun( 584 "function A(s) { this.s = s; }\n" 585 "function B(x) { this.x = x; }\n" 586 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n" 587 "var b = new B(a);"); 588 const v8::HeapSnapshot* snapshot = 589 v8::HeapProfiler::TakeSnapshot(v8_str("json")); 590 TestJSONStream stream; 591 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); 592 CHECK_GT(stream.size(), 0); 593 CHECK_EQ(1, stream.eos_signaled()); 594 i::ScopedVector<char> json(stream.size()); 595 stream.WriteTo(json); 596 597 // Verify that snapshot string is valid JSON. 598 AsciiResource json_res(json); 599 v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res); 600 env->Global()->Set(v8_str("json_snapshot"), json_string); 601 v8::Local<v8::Value> snapshot_parse_result = CompileRun( 602 "var parsed = JSON.parse(json_snapshot); true;"); 603 CHECK(!snapshot_parse_result.IsEmpty()); 604 605 // Verify that snapshot object has required fields. 606 v8::Local<v8::Object> parsed_snapshot = 607 env->Global()->Get(v8_str("parsed"))->ToObject(); 608 CHECK(parsed_snapshot->Has(v8_str("snapshot"))); 609 CHECK(parsed_snapshot->Has(v8_str("nodes"))); 610 CHECK(parsed_snapshot->Has(v8_str("strings"))); 611 612 // Get node and edge "member" offsets. 613 v8::Local<v8::Value> meta_analysis_result = CompileRun( 614 "var parsed_meta = parsed.nodes[0];\n" 615 "var children_count_offset =" 616 " parsed_meta.fields.indexOf('children_count');\n" 617 "var children_offset =" 618 " parsed_meta.fields.indexOf('children');\n" 619 "var children_meta =" 620 " parsed_meta.types[children_offset];\n" 621 "var child_fields_count = children_meta.fields.length;\n" 622 "var child_type_offset =" 623 " children_meta.fields.indexOf('type');\n" 624 "var child_name_offset =" 625 " children_meta.fields.indexOf('name_or_index');\n" 626 "var child_to_node_offset =" 627 " children_meta.fields.indexOf('to_node');\n" 628 "var property_type =" 629 " children_meta.types[child_type_offset].indexOf('property');\n" 630 "var shortcut_type =" 631 " children_meta.types[child_type_offset].indexOf('shortcut');"); 632 CHECK(!meta_analysis_result.IsEmpty()); 633 634 // A helper function for processing encoded nodes. 635 CompileRun( 636 "function GetChildPosByProperty(pos, prop_name, prop_type) {\n" 637 " var nodes = parsed.nodes;\n" 638 " var strings = parsed.strings;\n" 639 " for (var i = 0,\n" 640 " count = nodes[pos + children_count_offset] * child_fields_count;\n" 641 " i < count; i += child_fields_count) {\n" 642 " var child_pos = pos + children_offset + i;\n" 643 " if (nodes[child_pos + child_type_offset] === prop_type\n" 644 " && strings[nodes[child_pos + child_name_offset]] === prop_name)\n" 645 " return nodes[child_pos + child_to_node_offset];\n" 646 " }\n" 647 " return null;\n" 648 "}\n"); 649 // Get the string index using the path: <root> -> <global>.b.x.s 650 v8::Local<v8::Value> string_obj_pos_val = CompileRun( 651 "GetChildPosByProperty(\n" 652 " GetChildPosByProperty(\n" 653 " GetChildPosByProperty(" 654 " parsed.nodes[1 + children_offset + child_to_node_offset]," 655 " \"b\",shortcut_type),\n" 656 " \"x\", property_type)," 657 " \"s\", property_type)"); 658 CHECK(!string_obj_pos_val.IsEmpty()); 659 int string_obj_pos = 660 static_cast<int>(string_obj_pos_val->ToNumber()->Value()); 661 v8::Local<v8::Object> nodes_array = 662 parsed_snapshot->Get(v8_str("nodes"))->ToObject(); 663 int string_index = static_cast<int>( 664 nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value()); 665 CHECK_GT(string_index, 0); 666 v8::Local<v8::Object> strings_array = 667 parsed_snapshot->Get(v8_str("strings"))->ToObject(); 668 v8::Local<v8::String> string = strings_array->Get(string_index)->ToString(); 669 v8::Local<v8::String> ref_string = 670 CompileRun(STRING_LITERAL_FOR_TEST)->ToString(); 671 #undef STRING_LITERAL_FOR_TEST 672 CHECK_EQ(*v8::String::Utf8Value(ref_string), 673 *v8::String::Utf8Value(string)); 674 } 675 676 677 TEST(HeapSnapshotJSONSerializationAborting) { 678 v8::HandleScope scope; 679 LocalContext env; 680 const v8::HeapSnapshot* snapshot = 681 v8::HeapProfiler::TakeSnapshot(v8_str("abort")); 682 TestJSONStream stream(5); 683 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); 684 CHECK_GT(stream.size(), 0); 685 CHECK_EQ(0, stream.eos_signaled()); 686 } 687 688 689 static void CheckChildrenIds(const v8::HeapSnapshot* snapshot, 690 const v8::HeapGraphNode* node, 691 int level, int max_level) { 692 if (level > max_level) return; 693 CHECK_EQ(node, snapshot->GetNodeById(node->GetId())); 694 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { 695 const v8::HeapGraphEdge* prop = node->GetChild(i); 696 const v8::HeapGraphNode* child = 697 snapshot->GetNodeById(prop->GetToNode()->GetId()); 698 CHECK_EQ_UINT64_T(prop->GetToNode()->GetId(), child->GetId()); 699 CHECK_EQ(prop->GetToNode(), child); 700 CheckChildrenIds(snapshot, child, level + 1, max_level); 701 } 702 } 703 704 705 TEST(HeapSnapshotGetNodeById) { 706 v8::HandleScope scope; 707 LocalContext env; 708 709 const v8::HeapSnapshot* snapshot = 710 v8::HeapProfiler::TakeSnapshot(v8_str("id")); 711 const v8::HeapGraphNode* root = snapshot->GetRoot(); 712 CheckChildrenIds(snapshot, root, 0, 3); 713 // Check a big id, which should not exist yet. 714 CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL)); 715 } 716 717 718 namespace { 719 720 class TestActivityControl : public v8::ActivityControl { 721 public: 722 explicit TestActivityControl(int abort_count) 723 : done_(0), total_(0), abort_count_(abort_count) {} 724 ControlOption ReportProgressValue(int done, int total) { 725 done_ = done; 726 total_ = total; 727 return --abort_count_ != 0 ? kContinue : kAbort; 728 } 729 int done() { return done_; } 730 int total() { return total_; } 731 732 private: 733 int done_; 734 int total_; 735 int abort_count_; 736 }; 737 } 738 739 TEST(TakeHeapSnapshotAborting) { 740 v8::HandleScope scope; 741 LocalContext env; 742 743 const int snapshots_count = v8::HeapProfiler::GetSnapshotsCount(); 744 TestActivityControl aborting_control(1); 745 const v8::HeapSnapshot* no_snapshot = 746 v8::HeapProfiler::TakeSnapshot(v8_str("abort"), 747 v8::HeapSnapshot::kFull, 748 &aborting_control); 749 CHECK_EQ(NULL, no_snapshot); 750 CHECK_EQ(snapshots_count, v8::HeapProfiler::GetSnapshotsCount()); 751 CHECK_GT(aborting_control.total(), aborting_control.done()); 752 753 TestActivityControl control(-1); // Don't abort. 754 const v8::HeapSnapshot* snapshot = 755 v8::HeapProfiler::TakeSnapshot(v8_str("full"), 756 v8::HeapSnapshot::kFull, 757 &control); 758 CHECK_NE(NULL, snapshot); 759 CHECK_EQ(snapshots_count + 1, v8::HeapProfiler::GetSnapshotsCount()); 760 CHECK_EQ(control.total(), control.done()); 761 CHECK_GT(control.total(), 0); 762 } 763 764 765 namespace { 766 767 class TestRetainedObjectInfo : public v8::RetainedObjectInfo { 768 public: 769 TestRetainedObjectInfo(int hash, 770 const char* group_label, 771 const char* label, 772 intptr_t element_count = -1, 773 intptr_t size = -1) 774 : disposed_(false), 775 hash_(hash), 776 group_label_(group_label), 777 label_(label), 778 element_count_(element_count), 779 size_(size) { 780 instances.Add(this); 781 } 782 virtual ~TestRetainedObjectInfo() {} 783 virtual void Dispose() { 784 CHECK(!disposed_); 785 disposed_ = true; 786 } 787 virtual bool IsEquivalent(RetainedObjectInfo* other) { 788 return GetHash() == other->GetHash(); 789 } 790 virtual intptr_t GetHash() { return hash_; } 791 virtual const char* GetGroupLabel() { return group_label_; } 792 virtual const char* GetLabel() { return label_; } 793 virtual intptr_t GetElementCount() { return element_count_; } 794 virtual intptr_t GetSizeInBytes() { return size_; } 795 bool disposed() { return disposed_; } 796 797 static v8::RetainedObjectInfo* WrapperInfoCallback( 798 uint16_t class_id, v8::Handle<v8::Value> wrapper) { 799 if (class_id == 1) { 800 if (wrapper->IsString()) { 801 v8::String::AsciiValue ascii(wrapper); 802 if (strcmp(*ascii, "AAA") == 0) 803 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100); 804 else if (strcmp(*ascii, "BBB") == 0) 805 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100); 806 } 807 } else if (class_id == 2) { 808 if (wrapper->IsString()) { 809 v8::String::AsciiValue ascii(wrapper); 810 if (strcmp(*ascii, "CCC") == 0) 811 return new TestRetainedObjectInfo(2, "ccc-group", "ccc"); 812 } 813 } 814 CHECK(false); 815 return NULL; 816 } 817 818 static i::List<TestRetainedObjectInfo*> instances; 819 820 private: 821 bool disposed_; 822 int category_; 823 int hash_; 824 const char* group_label_; 825 const char* label_; 826 intptr_t element_count_; 827 intptr_t size_; 828 }; 829 830 831 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances; 832 } 833 834 835 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent, 836 v8::HeapGraphNode::Type type, 837 const char* name) { 838 for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) { 839 const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode(); 840 if (node->GetType() == type && strcmp(name, 841 const_cast<i::HeapEntry*>( 842 reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) { 843 return node; 844 } 845 } 846 return NULL; 847 } 848 849 850 TEST(HeapSnapshotRetainedObjectInfo) { 851 v8::HandleScope scope; 852 LocalContext env; 853 854 v8::HeapProfiler::DefineWrapperClass( 855 1, TestRetainedObjectInfo::WrapperInfoCallback); 856 v8::HeapProfiler::DefineWrapperClass( 857 2, TestRetainedObjectInfo::WrapperInfoCallback); 858 v8::Persistent<v8::String> p_AAA = 859 v8::Persistent<v8::String>::New(v8_str("AAA")); 860 p_AAA.SetWrapperClassId(1); 861 v8::Persistent<v8::String> p_BBB = 862 v8::Persistent<v8::String>::New(v8_str("BBB")); 863 p_BBB.SetWrapperClassId(1); 864 v8::Persistent<v8::String> p_CCC = 865 v8::Persistent<v8::String>::New(v8_str("CCC")); 866 p_CCC.SetWrapperClassId(2); 867 CHECK_EQ(0, TestRetainedObjectInfo::instances.length()); 868 const v8::HeapSnapshot* snapshot = 869 v8::HeapProfiler::TakeSnapshot(v8_str("retained")); 870 871 CHECK_EQ(3, TestRetainedObjectInfo::instances.length()); 872 for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) { 873 CHECK(TestRetainedObjectInfo::instances[i]->disposed()); 874 delete TestRetainedObjectInfo::instances[i]; 875 } 876 877 const v8::HeapGraphNode* native_group_aaa = GetNode( 878 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group"); 879 CHECK_NE(NULL, native_group_aaa); 880 CHECK_EQ(1, native_group_aaa->GetChildrenCount()); 881 const v8::HeapGraphNode* aaa = GetNode( 882 native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries"); 883 CHECK_NE(NULL, aaa); 884 CHECK_EQ(2, aaa->GetChildrenCount()); 885 886 const v8::HeapGraphNode* native_group_ccc = GetNode( 887 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group"); 888 const v8::HeapGraphNode* ccc = GetNode( 889 native_group_ccc, v8::HeapGraphNode::kNative, "ccc"); 890 CHECK_NE(NULL, ccc); 891 892 const v8::HeapGraphNode* n_AAA = GetNode( 893 aaa, v8::HeapGraphNode::kString, "AAA"); 894 CHECK_NE(NULL, n_AAA); 895 const v8::HeapGraphNode* n_BBB = GetNode( 896 aaa, v8::HeapGraphNode::kString, "BBB"); 897 CHECK_NE(NULL, n_BBB); 898 CHECK_EQ(1, ccc->GetChildrenCount()); 899 const v8::HeapGraphNode* n_CCC = GetNode( 900 ccc, v8::HeapGraphNode::kString, "CCC"); 901 CHECK_NE(NULL, n_CCC); 902 903 CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native")); 904 CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native")); 905 CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native")); 906 } 907 908 909 class GraphWithImplicitRefs { 910 public: 911 static const int kObjectsCount = 4; 912 explicit GraphWithImplicitRefs(LocalContext* env) { 913 CHECK_EQ(NULL, instance_); 914 instance_ = this; 915 for (int i = 0; i < kObjectsCount; i++) { 916 objects_[i] = v8::Persistent<v8::Object>::New(v8::Object::New()); 917 } 918 (*env)->Global()->Set(v8_str("root_object"), objects_[0]); 919 } 920 ~GraphWithImplicitRefs() { 921 instance_ = NULL; 922 } 923 924 static void gcPrologue() { 925 instance_->AddImplicitReferences(); 926 } 927 928 private: 929 void AddImplicitReferences() { 930 // 0 -> 1 931 v8::V8::AddImplicitReferences( 932 v8::Persistent<v8::Object>::Cast(objects_[0]), &objects_[1], 1); 933 // Adding two more references(note length=2 in params): 1 -> 2, 1 -> 3 934 v8::V8::AddImplicitReferences( 935 v8::Persistent<v8::Object>::Cast(objects_[1]), &objects_[2], 2); 936 } 937 938 v8::Persistent<v8::Value> objects_[kObjectsCount]; 939 static GraphWithImplicitRefs* instance_; 940 }; 941 942 GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL; 943 944 945 TEST(HeapSnapshotImplicitReferences) { 946 v8::HandleScope scope; 947 LocalContext env; 948 949 GraphWithImplicitRefs graph(&env); 950 v8::V8::SetGlobalGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue); 951 952 const v8::HeapSnapshot* snapshot = 953 v8::HeapProfiler::TakeSnapshot(v8_str("implicit_refs")); 954 955 const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot); 956 // Use kShortcut type to skip intermediate JSGlobalPropertyCell 957 const v8::HeapGraphNode* obj0 = GetProperty( 958 global_object, v8::HeapGraphEdge::kShortcut, "root_object"); 959 CHECK(obj0); 960 CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType()); 961 const v8::HeapGraphNode* obj1 = GetProperty( 962 obj0, v8::HeapGraphEdge::kInternal, "native"); 963 CHECK(obj1); 964 int implicit_targets_count = 0; 965 for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) { 966 const v8::HeapGraphEdge* prop = obj1->GetChild(i); 967 v8::String::AsciiValue prop_name(prop->GetName()); 968 if (prop->GetType() == v8::HeapGraphEdge::kInternal && 969 strcmp("native", *prop_name) == 0) { 970 ++implicit_targets_count; 971 } 972 } 973 CHECK_EQ(2, implicit_targets_count); 974 v8::V8::SetGlobalGCPrologueCallback(NULL); 975 } 976 977 978 TEST(DeleteAllHeapSnapshots) { 979 v8::HandleScope scope; 980 LocalContext env; 981 982 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount()); 983 v8::HeapProfiler::DeleteAllSnapshots(); 984 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount()); 985 CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1"))); 986 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount()); 987 v8::HeapProfiler::DeleteAllSnapshots(); 988 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount()); 989 CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1"))); 990 CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("2"))); 991 CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount()); 992 v8::HeapProfiler::DeleteAllSnapshots(); 993 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount()); 994 } 995 996 997 TEST(DeleteHeapSnapshot) { 998 v8::HandleScope scope; 999 LocalContext env; 1000 1001 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount()); 1002 const v8::HeapSnapshot* s1 = 1003 v8::HeapProfiler::TakeSnapshot(v8_str("1")); 1004 CHECK_NE(NULL, s1); 1005 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount()); 1006 unsigned uid1 = s1->GetUid(); 1007 CHECK_EQ(s1, v8::HeapProfiler::FindSnapshot(uid1)); 1008 const_cast<v8::HeapSnapshot*>(s1)->Delete(); 1009 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount()); 1010 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid1)); 1011 1012 const v8::HeapSnapshot* s2 = 1013 v8::HeapProfiler::TakeSnapshot(v8_str("2")); 1014 CHECK_NE(NULL, s2); 1015 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount()); 1016 unsigned uid2 = s2->GetUid(); 1017 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2)); 1018 CHECK_EQ(s2, v8::HeapProfiler::FindSnapshot(uid2)); 1019 const v8::HeapSnapshot* s3 = 1020 v8::HeapProfiler::TakeSnapshot(v8_str("3")); 1021 CHECK_NE(NULL, s3); 1022 CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount()); 1023 unsigned uid3 = s3->GetUid(); 1024 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3)); 1025 CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3)); 1026 const_cast<v8::HeapSnapshot*>(s2)->Delete(); 1027 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount()); 1028 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid2)); 1029 CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3)); 1030 const_cast<v8::HeapSnapshot*>(s3)->Delete(); 1031 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount()); 1032 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid3)); 1033 } 1034 1035 1036 TEST(DocumentURL) { 1037 v8::HandleScope scope; 1038 LocalContext env; 1039 1040 CompileRun("document = { URL:\"abcdefgh\" };"); 1041 1042 const v8::HeapSnapshot* snapshot = 1043 v8::HeapProfiler::TakeSnapshot(v8_str("document")); 1044 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1045 CHECK_NE(NULL, global); 1046 CHECK_EQ("Object / abcdefgh", 1047 const_cast<i::HeapEntry*>( 1048 reinterpret_cast<const i::HeapEntry*>(global))->name()); 1049 } 1050 1051 1052 TEST(DocumentWithException) { 1053 v8::HandleScope scope; 1054 LocalContext env; 1055 1056 CompileRun( 1057 "this.__defineGetter__(\"document\", function() { throw new Error(); })"); 1058 const v8::HeapSnapshot* snapshot = 1059 v8::HeapProfiler::TakeSnapshot(v8_str("document")); 1060 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1061 CHECK_NE(NULL, global); 1062 CHECK_EQ("Object", 1063 const_cast<i::HeapEntry*>( 1064 reinterpret_cast<const i::HeapEntry*>(global))->name()); 1065 } 1066 1067 1068 TEST(DocumentURLWithException) { 1069 v8::HandleScope scope; 1070 LocalContext env; 1071 1072 CompileRun( 1073 "function URLWithException() {}\n" 1074 "URLWithException.prototype = { get URL() { throw new Error(); } };\n" 1075 "document = { URL: new URLWithException() };"); 1076 const v8::HeapSnapshot* snapshot = 1077 v8::HeapProfiler::TakeSnapshot(v8_str("document")); 1078 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1079 CHECK_NE(NULL, global); 1080 CHECK_EQ("Object", 1081 const_cast<i::HeapEntry*>( 1082 reinterpret_cast<const i::HeapEntry*>(global))->name()); 1083 } 1084 1085 1086 TEST(NoHandleLeaks) { 1087 v8::HandleScope scope; 1088 LocalContext env; 1089 1090 CompileRun("document = { URL:\"abcdefgh\" };"); 1091 1092 v8::Handle<v8::String> name(v8_str("leakz")); 1093 int count_before = i::HandleScope::NumberOfHandles(); 1094 v8::HeapProfiler::TakeSnapshot(name); 1095 int count_after = i::HandleScope::NumberOfHandles(); 1096 CHECK_EQ(count_before, count_after); 1097 } 1098 1099 1100 TEST(NodesIteration) { 1101 v8::HandleScope scope; 1102 LocalContext env; 1103 const v8::HeapSnapshot* snapshot = 1104 v8::HeapProfiler::TakeSnapshot(v8_str("iteration")); 1105 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1106 CHECK_NE(NULL, global); 1107 // Verify that we can find this object by iteration. 1108 const int nodes_count = snapshot->GetNodesCount(); 1109 int count = 0; 1110 for (int i = 0; i < nodes_count; ++i) { 1111 if (snapshot->GetNode(i) == global) 1112 ++count; 1113 } 1114 CHECK_EQ(1, count); 1115 } 1116 1117 1118 TEST(GetHeapValue) { 1119 v8::HandleScope scope; 1120 LocalContext env; 1121 1122 CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };"); 1123 const v8::HeapSnapshot* snapshot = 1124 v8::HeapProfiler::TakeSnapshot(v8_str("value")); 1125 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1126 CHECK(global->GetHeapValue()->IsObject()); 1127 v8::Local<v8::Object> js_global = 1128 env->Global()->GetPrototype().As<v8::Object>(); 1129 CHECK(js_global == global->GetHeapValue()); 1130 const v8::HeapGraphNode* obj = GetProperty( 1131 global, v8::HeapGraphEdge::kShortcut, "a"); 1132 CHECK(obj->GetHeapValue()->IsObject()); 1133 v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>(); 1134 CHECK(js_obj == obj->GetHeapValue()); 1135 const v8::HeapGraphNode* s_prop = 1136 GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop"); 1137 v8::Local<v8::String> js_s_prop = 1138 js_obj->Get(v8_str("s_prop")).As<v8::String>(); 1139 CHECK(js_s_prop == s_prop->GetHeapValue()); 1140 const v8::HeapGraphNode* n_prop = 1141 GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop"); 1142 v8::Local<v8::Number> js_n_prop = 1143 js_obj->Get(v8_str("n_prop")).As<v8::Number>(); 1144 CHECK(js_n_prop == n_prop->GetHeapValue()); 1145 } 1146 1147 1148 TEST(GetHeapValueForDeletedObject) { 1149 v8::HandleScope scope; 1150 LocalContext env; 1151 1152 // It is impossible to delete a global property, so we are about to delete a 1153 // property of the "a" object. Also, the "p" object can't be an empty one 1154 // because the empty object is static and isn't actually deleted. 1155 CompileRun("a = { p: { r: {} } };"); 1156 const v8::HeapSnapshot* snapshot = 1157 v8::HeapProfiler::TakeSnapshot(v8_str("snapshot")); 1158 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1159 const v8::HeapGraphNode* obj = GetProperty( 1160 global, v8::HeapGraphEdge::kShortcut, "a"); 1161 const v8::HeapGraphNode* prop = GetProperty( 1162 obj, v8::HeapGraphEdge::kProperty, "p"); 1163 { 1164 // Perform the check inside a nested local scope to avoid creating a 1165 // reference to the object we are deleting. 1166 v8::HandleScope scope; 1167 CHECK(prop->GetHeapValue()->IsObject()); 1168 } 1169 CompileRun("delete a.p;"); 1170 CHECK(prop->GetHeapValue()->IsUndefined()); 1171 } 1172 1173 1174 static int StringCmp(const char* ref, i::String* act) { 1175 i::SmartArrayPointer<char> s_act = act->ToCString(); 1176 int result = strcmp(ref, *s_act); 1177 if (result != 0) 1178 fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, *s_act); 1179 return result; 1180 } 1181 1182 1183 TEST(GetConstructorName) { 1184 v8::HandleScope scope; 1185 LocalContext env; 1186 1187 CompileRun( 1188 "function Constructor1() {};\n" 1189 "var obj1 = new Constructor1();\n" 1190 "var Constructor2 = function() {};\n" 1191 "var obj2 = new Constructor2();\n" 1192 "var obj3 = {};\n" 1193 "obj3.constructor = function Constructor3() {};\n" 1194 "var obj4 = {};\n" 1195 "// Slow properties\n" 1196 "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n" 1197 "obj4.constructor = function Constructor4() {};\n" 1198 "var obj5 = {};\n" 1199 "var obj6 = {};\n" 1200 "obj6.constructor = 6;"); 1201 v8::Local<v8::Object> js_global = 1202 env->Global()->GetPrototype().As<v8::Object>(); 1203 v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>(); 1204 i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1); 1205 CHECK_EQ(0, StringCmp( 1206 "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1))); 1207 v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>(); 1208 i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2); 1209 CHECK_EQ(0, StringCmp( 1210 "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2))); 1211 v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>(); 1212 i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3); 1213 CHECK_EQ(0, StringCmp( 1214 "Constructor3", i::V8HeapExplorer::GetConstructorName(*js_obj3))); 1215 v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>(); 1216 i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4); 1217 CHECK_EQ(0, StringCmp( 1218 "Constructor4", i::V8HeapExplorer::GetConstructorName(*js_obj4))); 1219 v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>(); 1220 i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5); 1221 CHECK_EQ(0, StringCmp( 1222 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5))); 1223 v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>(); 1224 i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6); 1225 CHECK_EQ(0, StringCmp( 1226 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6))); 1227 } 1228 1229 1230 TEST(FastCaseGetter) { 1231 v8::HandleScope scope; 1232 LocalContext env; 1233 1234 CompileRun("var obj1 = {};\n" 1235 "obj1.__defineGetter__('propWithGetter', function Y() {\n" 1236 " return 42;\n" 1237 "});\n" 1238 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n" 1239 " return this.value_ = value;\n" 1240 "});\n"); 1241 const v8::HeapSnapshot* snapshot = 1242 v8::HeapProfiler::TakeSnapshot(v8_str("fastCaseGetter")); 1243 1244 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1245 CHECK_NE(NULL, global); 1246 const v8::HeapGraphNode* obj1 = 1247 GetProperty(global, v8::HeapGraphEdge::kShortcut, "obj1"); 1248 CHECK_NE(NULL, obj1); 1249 const v8::HeapGraphNode* getterFunction = 1250 GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get-propWithGetter"); 1251 CHECK_NE(NULL, getterFunction); 1252 const v8::HeapGraphNode* setterFunction = 1253 GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set-propWithSetter"); 1254 CHECK_NE(NULL, setterFunction); 1255 } 1256 1257 1258 bool HasWeakEdge(const v8::HeapGraphNode* node) { 1259 for (int i = 0; i < node->GetChildrenCount(); ++i) { 1260 const v8::HeapGraphEdge* handle_edge = node->GetChild(i); 1261 if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true; 1262 } 1263 return false; 1264 } 1265 1266 1267 bool HasWeakGlobalHandle() { 1268 const v8::HeapSnapshot* snapshot = 1269 v8::HeapProfiler::TakeSnapshot(v8_str("weaks")); 1270 const v8::HeapGraphNode* gc_roots = GetNode( 1271 snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)"); 1272 CHECK_NE(NULL, gc_roots); 1273 const v8::HeapGraphNode* global_handles = GetNode( 1274 gc_roots, v8::HeapGraphNode::kObject, "(Global handles)"); 1275 CHECK_NE(NULL, global_handles); 1276 return HasWeakEdge(global_handles); 1277 } 1278 1279 1280 static void PersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) { 1281 handle.Dispose(); 1282 } 1283 1284 1285 TEST(WeakGlobalHandle) { 1286 v8::HandleScope scope; 1287 LocalContext env; 1288 1289 CHECK(!HasWeakGlobalHandle()); 1290 1291 v8::Persistent<v8::Object> handle = 1292 v8::Persistent<v8::Object>::New(v8::Object::New()); 1293 handle.MakeWeak(NULL, PersistentHandleCallback); 1294 1295 CHECK(HasWeakGlobalHandle()); 1296 } 1297 1298 1299 TEST(WeakGlobalContextRefs) { 1300 v8::HandleScope scope; 1301 LocalContext env; 1302 1303 const v8::HeapSnapshot* snapshot = 1304 v8::HeapProfiler::TakeSnapshot(v8_str("weaks")); 1305 const v8::HeapGraphNode* gc_roots = GetNode( 1306 snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)"); 1307 CHECK_NE(NULL, gc_roots); 1308 const v8::HeapGraphNode* global_handles = GetNode( 1309 gc_roots, v8::HeapGraphNode::kObject, "(Global handles)"); 1310 CHECK_NE(NULL, global_handles); 1311 const v8::HeapGraphNode* global_context = GetNode( 1312 global_handles, v8::HeapGraphNode::kHidden, "system / GlobalContext"); 1313 CHECK_NE(NULL, global_context); 1314 CHECK(HasWeakEdge(global_context)); 1315 } 1316 1317 1318 TEST(SfiAndJsFunctionWeakRefs) { 1319 v8::HandleScope scope; 1320 LocalContext env; 1321 1322 CompileRun( 1323 "fun = (function (x) { return function () { return x + 1; } })(1);"); 1324 const v8::HeapSnapshot* snapshot = 1325 v8::HeapProfiler::TakeSnapshot(v8_str("fun")); 1326 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1327 CHECK_NE(NULL, global); 1328 const v8::HeapGraphNode* fun = 1329 GetProperty(global, v8::HeapGraphEdge::kShortcut, "fun"); 1330 CHECK(HasWeakEdge(fun)); 1331 const v8::HeapGraphNode* shared = 1332 GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared"); 1333 CHECK(HasWeakEdge(shared)); 1334 } 1335 1336 1337 TEST(PersistentHandleCount) { 1338 v8::HandleScope scope; 1339 LocalContext env; 1340 1341 // V8 also uses global handles internally, so we can't test for an absolute 1342 // number. 1343 int global_handle_count = v8::HeapProfiler::GetPersistentHandleCount(); 1344 1345 // Create some persistent handles. 1346 v8::Persistent<v8::String> p_AAA = 1347 v8::Persistent<v8::String>::New(v8_str("AAA")); 1348 CHECK_EQ(global_handle_count + 1, 1349 v8::HeapProfiler::GetPersistentHandleCount()); 1350 v8::Persistent<v8::String> p_BBB = 1351 v8::Persistent<v8::String>::New(v8_str("BBB")); 1352 CHECK_EQ(global_handle_count + 2, 1353 v8::HeapProfiler::GetPersistentHandleCount()); 1354 v8::Persistent<v8::String> p_CCC = 1355 v8::Persistent<v8::String>::New(v8_str("CCC")); 1356 CHECK_EQ(global_handle_count + 3, 1357 v8::HeapProfiler::GetPersistentHandleCount()); 1358 1359 // Dipose the persistent handles in a different order. 1360 p_AAA.Dispose(); 1361 CHECK_EQ(global_handle_count + 2, 1362 v8::HeapProfiler::GetPersistentHandleCount()); 1363 p_CCC.Dispose(); 1364 CHECK_EQ(global_handle_count + 1, 1365 v8::HeapProfiler::GetPersistentHandleCount()); 1366 p_BBB.Dispose(); 1367 CHECK_EQ(global_handle_count, v8::HeapProfiler::GetPersistentHandleCount()); 1368 } 1369