1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 // 28 // Tests for heap profiler 29 30 #include <ctype.h> 31 32 #include "src/v8.h" 33 34 #include "include/v8-profiler.h" 35 #include "src/allocation-tracker.h" 36 #include "src/debug.h" 37 #include "src/hashmap.h" 38 #include "src/heap-profiler.h" 39 #include "src/snapshot.h" 40 #include "src/utils-inl.h" 41 #include "test/cctest/cctest.h" 42 43 using i::AllocationTraceNode; 44 using i::AllocationTraceTree; 45 using i::AllocationTracker; 46 using i::HashMap; 47 using i::Vector; 48 49 namespace { 50 51 class NamedEntriesDetector { 52 public: 53 NamedEntriesDetector() 54 : has_A2(false), has_B2(false), has_C2(false) { 55 } 56 57 void CheckEntry(i::HeapEntry* entry) { 58 if (strcmp(entry->name(), "A2") == 0) has_A2 = true; 59 if (strcmp(entry->name(), "B2") == 0) has_B2 = true; 60 if (strcmp(entry->name(), "C2") == 0) has_C2 = true; 61 } 62 63 static bool AddressesMatch(void* key1, void* key2) { 64 return key1 == key2; 65 } 66 67 void CheckAllReachables(i::HeapEntry* root) { 68 i::HashMap visited(AddressesMatch); 69 i::List<i::HeapEntry*> list(10); 70 list.Add(root); 71 CheckEntry(root); 72 while (!list.is_empty()) { 73 i::HeapEntry* entry = list.RemoveLast(); 74 i::Vector<i::HeapGraphEdge*> children = entry->children(); 75 for (int i = 0; i < children.length(); ++i) { 76 if (children[i]->type() == i::HeapGraphEdge::kShortcut) continue; 77 i::HeapEntry* child = children[i]->to(); 78 i::HashMap::Entry* entry = visited.Lookup( 79 reinterpret_cast<void*>(child), 80 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)), 81 true); 82 if (entry->value) 83 continue; 84 entry->value = reinterpret_cast<void*>(1); 85 list.Add(child); 86 CheckEntry(child); 87 } 88 } 89 } 90 91 bool has_A2; 92 bool has_B2; 93 bool has_C2; 94 }; 95 96 } // namespace 97 98 99 static const v8::HeapGraphNode* GetGlobalObject( 100 const v8::HeapSnapshot* snapshot) { 101 CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount()); 102 // The 0th-child is (GC Roots), 1st is the user root. 103 const v8::HeapGraphNode* global_obj = 104 snapshot->GetRoot()->GetChild(1)->GetToNode(); 105 CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>( 106 reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6)); 107 return global_obj; 108 } 109 110 111 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node, 112 v8::HeapGraphEdge::Type type, 113 const char* name) { 114 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { 115 const v8::HeapGraphEdge* prop = node->GetChild(i); 116 v8::String::Utf8Value prop_name(prop->GetName()); 117 if (prop->GetType() == type && strcmp(name, *prop_name) == 0) 118 return prop->GetToNode(); 119 } 120 return NULL; 121 } 122 123 124 static bool HasString(const v8::HeapGraphNode* node, const char* contents) { 125 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { 126 const v8::HeapGraphEdge* prop = node->GetChild(i); 127 const v8::HeapGraphNode* node = prop->GetToNode(); 128 if (node->GetType() == v8::HeapGraphNode::kString) { 129 v8::String::Utf8Value node_name(node->GetName()); 130 if (strcmp(contents, *node_name) == 0) return true; 131 } 132 } 133 return false; 134 } 135 136 137 static bool AddressesMatch(void* key1, void* key2) { 138 return key1 == key2; 139 } 140 141 142 // Check that snapshot has no unretained entries except root. 143 static bool ValidateSnapshot(const v8::HeapSnapshot* snapshot, int depth = 3) { 144 i::HeapSnapshot* heap_snapshot = const_cast<i::HeapSnapshot*>( 145 reinterpret_cast<const i::HeapSnapshot*>(snapshot)); 146 147 i::HashMap visited(AddressesMatch); 148 i::List<i::HeapGraphEdge>& edges = heap_snapshot->edges(); 149 for (int i = 0; i < edges.length(); ++i) { 150 i::HashMap::Entry* entry = visited.Lookup( 151 reinterpret_cast<void*>(edges[i].to()), 152 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(edges[i].to())), 153 true); 154 uint32_t ref_count = static_cast<uint32_t>( 155 reinterpret_cast<uintptr_t>(entry->value)); 156 entry->value = reinterpret_cast<void*>(ref_count + 1); 157 } 158 uint32_t unretained_entries_count = 0; 159 i::List<i::HeapEntry>& entries = heap_snapshot->entries(); 160 for (int i = 0; i < entries.length(); ++i) { 161 i::HashMap::Entry* entry = visited.Lookup( 162 reinterpret_cast<void*>(&entries[i]), 163 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&entries[i])), 164 false); 165 if (!entry && entries[i].id() != 1) { 166 entries[i].Print("entry with no retainer", "", depth, 0); 167 ++unretained_entries_count; 168 } 169 } 170 return unretained_entries_count == 0; 171 } 172 173 174 TEST(HeapSnapshot) { 175 LocalContext env2; 176 v8::HandleScope scope(env2->GetIsolate()); 177 v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler(); 178 179 CompileRun( 180 "function A2() {}\n" 181 "function B2(x) { return function() { return typeof x; }; }\n" 182 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n" 183 "var a2 = new A2();\n" 184 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n" 185 "var c2 = new C2(a2);"); 186 const v8::HeapSnapshot* snapshot_env2 = 187 heap_profiler->TakeHeapSnapshot(v8_str("env2")); 188 CHECK(ValidateSnapshot(snapshot_env2)); 189 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2); 190 191 // Verify, that JS global object of env2 has '..2' properties. 192 const v8::HeapGraphNode* a2_node = 193 GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2"); 194 CHECK_NE(NULL, a2_node); 195 CHECK_NE( 196 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1")); 197 CHECK_NE( 198 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2")); 199 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2")); 200 201 NamedEntriesDetector det; 202 det.CheckAllReachables(const_cast<i::HeapEntry*>( 203 reinterpret_cast<const i::HeapEntry*>(global_env2))); 204 CHECK(det.has_A2); 205 CHECK(det.has_B2); 206 CHECK(det.has_C2); 207 } 208 209 210 TEST(HeapSnapshotObjectSizes) { 211 LocalContext env; 212 v8::HandleScope scope(env->GetIsolate()); 213 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 214 215 // -a-> X1 --a 216 // x -b-> X2 <-| 217 CompileRun( 218 "function X(a, b) { this.a = a; this.b = b; }\n" 219 "x = new X(new X(), new X());\n" 220 "dummy = new X();\n" 221 "(function() { x.a.a = x.b; })();"); 222 const v8::HeapSnapshot* snapshot = 223 heap_profiler->TakeHeapSnapshot(v8_str("sizes")); 224 CHECK(ValidateSnapshot(snapshot)); 225 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 226 const v8::HeapGraphNode* x = 227 GetProperty(global, v8::HeapGraphEdge::kProperty, "x"); 228 CHECK_NE(NULL, x); 229 const v8::HeapGraphNode* x1 = 230 GetProperty(x, v8::HeapGraphEdge::kProperty, "a"); 231 CHECK_NE(NULL, x1); 232 const v8::HeapGraphNode* x2 = 233 GetProperty(x, v8::HeapGraphEdge::kProperty, "b"); 234 CHECK_NE(NULL, x2); 235 236 // Test sizes. 237 CHECK_NE(0, static_cast<int>(x->GetShallowSize())); 238 CHECK_NE(0, static_cast<int>(x1->GetShallowSize())); 239 CHECK_NE(0, static_cast<int>(x2->GetShallowSize())); 240 } 241 242 243 TEST(BoundFunctionInSnapshot) { 244 LocalContext env; 245 v8::HandleScope scope(env->GetIsolate()); 246 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 247 CompileRun( 248 "function myFunction(a, b) { this.a = a; this.b = b; }\n" 249 "function AAAAA() {}\n" 250 "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n"); 251 const v8::HeapSnapshot* snapshot = 252 heap_profiler->TakeHeapSnapshot(v8_str("sizes")); 253 CHECK(ValidateSnapshot(snapshot)); 254 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 255 const v8::HeapGraphNode* f = 256 GetProperty(global, v8::HeapGraphEdge::kProperty, "boundFunction"); 257 CHECK(f); 258 CHECK_EQ(v8::String::NewFromUtf8(env->GetIsolate(), "native_bind"), 259 f->GetName()); 260 const v8::HeapGraphNode* bindings = 261 GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings"); 262 CHECK_NE(NULL, bindings); 263 CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType()); 264 CHECK_EQ(4, bindings->GetChildrenCount()); 265 266 const v8::HeapGraphNode* bound_this = GetProperty( 267 f, v8::HeapGraphEdge::kShortcut, "bound_this"); 268 CHECK(bound_this); 269 CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType()); 270 271 const v8::HeapGraphNode* bound_function = GetProperty( 272 f, v8::HeapGraphEdge::kShortcut, "bound_function"); 273 CHECK(bound_function); 274 CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType()); 275 276 const v8::HeapGraphNode* bound_argument = GetProperty( 277 f, v8::HeapGraphEdge::kShortcut, "bound_argument_1"); 278 CHECK(bound_argument); 279 CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType()); 280 } 281 282 283 TEST(HeapSnapshotEntryChildren) { 284 LocalContext env; 285 v8::HandleScope scope(env->GetIsolate()); 286 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 287 288 CompileRun( 289 "function A() { }\n" 290 "a = new A;"); 291 const v8::HeapSnapshot* snapshot = 292 heap_profiler->TakeHeapSnapshot(v8_str("children")); 293 CHECK(ValidateSnapshot(snapshot)); 294 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 295 for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) { 296 const v8::HeapGraphEdge* prop = global->GetChild(i); 297 CHECK_EQ(global, prop->GetFromNode()); 298 } 299 const v8::HeapGraphNode* a = 300 GetProperty(global, v8::HeapGraphEdge::kProperty, "a"); 301 CHECK_NE(NULL, a); 302 for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) { 303 const v8::HeapGraphEdge* prop = a->GetChild(i); 304 CHECK_EQ(a, prop->GetFromNode()); 305 } 306 } 307 308 309 TEST(HeapSnapshotCodeObjects) { 310 LocalContext env; 311 v8::HandleScope scope(env->GetIsolate()); 312 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 313 314 CompileRun( 315 "function lazy(x) { return x - 1; }\n" 316 "function compiled(x) { return x + 1; }\n" 317 "var anonymous = (function() { return function() { return 0; } })();\n" 318 "compiled(1)"); 319 const v8::HeapSnapshot* snapshot = 320 heap_profiler->TakeHeapSnapshot(v8_str("code")); 321 CHECK(ValidateSnapshot(snapshot)); 322 323 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 324 const v8::HeapGraphNode* compiled = 325 GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled"); 326 CHECK_NE(NULL, compiled); 327 CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType()); 328 const v8::HeapGraphNode* lazy = 329 GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy"); 330 CHECK_NE(NULL, lazy); 331 CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType()); 332 const v8::HeapGraphNode* anonymous = 333 GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous"); 334 CHECK_NE(NULL, anonymous); 335 CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType()); 336 v8::String::Utf8Value anonymous_name(anonymous->GetName()); 337 CHECK_EQ("", *anonymous_name); 338 339 // Find references to code. 340 const v8::HeapGraphNode* compiled_code = 341 GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared"); 342 CHECK_NE(NULL, compiled_code); 343 const v8::HeapGraphNode* lazy_code = 344 GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared"); 345 CHECK_NE(NULL, lazy_code); 346 347 // Check that there's no strong next_code_link. There might be a weak one 348 // but might be not, so we can't check that fact. 349 const v8::HeapGraphNode* code = 350 GetProperty(compiled_code, v8::HeapGraphEdge::kInternal, "code"); 351 CHECK_NE(NULL, code); 352 const v8::HeapGraphNode* next_code_link = 353 GetProperty(code, v8::HeapGraphEdge::kInternal, "code"); 354 CHECK_EQ(NULL, next_code_link); 355 356 // Verify that non-compiled code doesn't contain references to "x" 357 // literal, while compiled code does. The scope info is stored in FixedArray 358 // objects attached to the SharedFunctionInfo. 359 bool compiled_references_x = false, lazy_references_x = false; 360 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) { 361 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i); 362 const v8::HeapGraphNode* node = prop->GetToNode(); 363 if (node->GetType() == v8::HeapGraphNode::kArray) { 364 if (HasString(node, "x")) { 365 compiled_references_x = true; 366 break; 367 } 368 } 369 } 370 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) { 371 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i); 372 const v8::HeapGraphNode* node = prop->GetToNode(); 373 if (node->GetType() == v8::HeapGraphNode::kArray) { 374 if (HasString(node, "x")) { 375 lazy_references_x = true; 376 break; 377 } 378 } 379 } 380 CHECK(compiled_references_x); 381 CHECK(!lazy_references_x); 382 } 383 384 385 TEST(HeapSnapshotHeapNumbers) { 386 LocalContext env; 387 v8::HandleScope scope(env->GetIsolate()); 388 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 389 CompileRun( 390 "a = 1; // a is Smi\n" 391 "b = 2.5; // b is HeapNumber"); 392 const v8::HeapSnapshot* snapshot = 393 heap_profiler->TakeHeapSnapshot(v8_str("numbers")); 394 CHECK(ValidateSnapshot(snapshot)); 395 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 396 CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a")); 397 const v8::HeapGraphNode* b = 398 GetProperty(global, v8::HeapGraphEdge::kProperty, "b"); 399 CHECK_NE(NULL, b); 400 CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType()); 401 } 402 403 404 TEST(HeapSnapshotSlicedString) { 405 LocalContext env; 406 v8::HandleScope scope(env->GetIsolate()); 407 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 408 CompileRun( 409 "parent_string = \"123456789.123456789.123456789.123456789.123456789." 410 "123456789.123456789.123456789.123456789.123456789." 411 "123456789.123456789.123456789.123456789.123456789." 412 "123456789.123456789.123456789.123456789.123456789.\";" 413 "child_string = parent_string.slice(100);"); 414 const v8::HeapSnapshot* snapshot = 415 heap_profiler->TakeHeapSnapshot(v8_str("strings")); 416 CHECK(ValidateSnapshot(snapshot)); 417 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 418 const v8::HeapGraphNode* parent_string = 419 GetProperty(global, v8::HeapGraphEdge::kProperty, "parent_string"); 420 CHECK_NE(NULL, parent_string); 421 const v8::HeapGraphNode* child_string = 422 GetProperty(global, v8::HeapGraphEdge::kProperty, "child_string"); 423 CHECK_NE(NULL, child_string); 424 CHECK_EQ(v8::HeapGraphNode::kSlicedString, child_string->GetType()); 425 const v8::HeapGraphNode* parent = 426 GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent"); 427 CHECK_EQ(parent_string, parent); 428 heap_profiler->DeleteAllHeapSnapshots(); 429 } 430 431 432 TEST(HeapSnapshotConsString) { 433 v8::Isolate* isolate = CcTest::isolate(); 434 v8::HandleScope scope(isolate); 435 v8::Local<v8::ObjectTemplate> global_template = 436 v8::ObjectTemplate::New(isolate); 437 global_template->SetInternalFieldCount(1); 438 LocalContext env(NULL, global_template); 439 v8::Handle<v8::Object> global_proxy = env->Global(); 440 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 441 CHECK_EQ(1, global->InternalFieldCount()); 442 443 i::Factory* factory = CcTest::i_isolate()->factory(); 444 i::Handle<i::String> first = factory->NewStringFromStaticAscii("0123456789"); 445 i::Handle<i::String> second = factory->NewStringFromStaticAscii("0123456789"); 446 i::Handle<i::String> cons_string = 447 factory->NewConsString(first, second).ToHandleChecked(); 448 449 global->SetInternalField(0, v8::ToApiHandle<v8::String>(cons_string)); 450 451 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 452 const v8::HeapSnapshot* snapshot = 453 heap_profiler->TakeHeapSnapshot(v8_str("cons_strings")); 454 CHECK(ValidateSnapshot(snapshot)); 455 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot); 456 457 const v8::HeapGraphNode* string_node = 458 GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"); 459 CHECK_NE(NULL, string_node); 460 CHECK_EQ(v8::HeapGraphNode::kConsString, string_node->GetType()); 461 462 const v8::HeapGraphNode* first_node = 463 GetProperty(string_node, v8::HeapGraphEdge::kInternal, "first"); 464 CHECK_EQ(v8::HeapGraphNode::kString, first_node->GetType()); 465 466 const v8::HeapGraphNode* second_node = 467 GetProperty(string_node, v8::HeapGraphEdge::kInternal, "second"); 468 CHECK_EQ(v8::HeapGraphNode::kString, second_node->GetType()); 469 470 heap_profiler->DeleteAllHeapSnapshots(); 471 } 472 473 474 TEST(HeapSnapshotSymbol) { 475 i::FLAG_harmony_symbols = true; 476 477 LocalContext env; 478 v8::HandleScope scope(env->GetIsolate()); 479 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 480 481 CompileRun("a = Symbol('mySymbol');\n"); 482 const v8::HeapSnapshot* snapshot = 483 heap_profiler->TakeHeapSnapshot(v8_str("Symbol")); 484 CHECK(ValidateSnapshot(snapshot)); 485 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 486 const v8::HeapGraphNode* a = 487 GetProperty(global, v8::HeapGraphEdge::kProperty, "a"); 488 CHECK_NE(NULL, a); 489 CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol); 490 CHECK_EQ(v8_str("symbol"), a->GetName()); 491 const v8::HeapGraphNode* name = 492 GetProperty(a, v8::HeapGraphEdge::kInternal, "name"); 493 CHECK_NE(NULL, name); 494 CHECK_EQ(v8_str("mySymbol"), name->GetName()); 495 } 496 497 498 TEST(HeapSnapshotWeakCollection) { 499 i::FLAG_harmony_collections = true; 500 501 LocalContext env; 502 v8::HandleScope scope(env->GetIsolate()); 503 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 504 505 CompileRun("k = {}; v = {};\n" 506 "ws = new WeakSet(); ws.add(k); ws.add(v);\n" 507 "wm = new WeakMap(); wm.set(k, v);\n"); 508 const v8::HeapSnapshot* snapshot = 509 heap_profiler->TakeHeapSnapshot(v8_str("WeakCollections")); 510 CHECK(ValidateSnapshot(snapshot)); 511 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 512 const v8::HeapGraphNode* k = 513 GetProperty(global, v8::HeapGraphEdge::kProperty, "k"); 514 CHECK_NE(NULL, k); 515 const v8::HeapGraphNode* v = 516 GetProperty(global, v8::HeapGraphEdge::kProperty, "v"); 517 CHECK_NE(NULL, v); 518 519 const v8::HeapGraphNode* ws = 520 GetProperty(global, v8::HeapGraphEdge::kProperty, "ws"); 521 CHECK_NE(NULL, ws); 522 CHECK_EQ(v8::HeapGraphNode::kObject, ws->GetType()); 523 CHECK_EQ(v8_str("WeakSet"), ws->GetName()); 524 525 const v8::HeapGraphNode* ws_table = 526 GetProperty(ws, v8::HeapGraphEdge::kInternal, "table"); 527 CHECK_EQ(v8::HeapGraphNode::kArray, ws_table->GetType()); 528 CHECK_GT(ws_table->GetChildrenCount(), 0); 529 int weak_entries = 0; 530 for (int i = 0, count = ws_table->GetChildrenCount(); i < count; ++i) { 531 const v8::HeapGraphEdge* prop = ws_table->GetChild(i); 532 if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue; 533 if (k->GetId() == prop->GetToNode()->GetId()) { 534 ++weak_entries; 535 } 536 } 537 CHECK_EQ(1, weak_entries); 538 539 const v8::HeapGraphNode* wm = 540 GetProperty(global, v8::HeapGraphEdge::kProperty, "wm"); 541 CHECK_NE(NULL, wm); 542 CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType()); 543 CHECK_EQ(v8_str("WeakMap"), wm->GetName()); 544 545 const v8::HeapGraphNode* wm_table = 546 GetProperty(wm, v8::HeapGraphEdge::kInternal, "table"); 547 CHECK_EQ(v8::HeapGraphNode::kArray, wm_table->GetType()); 548 CHECK_GT(wm_table->GetChildrenCount(), 0); 549 weak_entries = 0; 550 for (int i = 0, count = wm_table->GetChildrenCount(); i < count; ++i) { 551 const v8::HeapGraphEdge* prop = wm_table->GetChild(i); 552 if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue; 553 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId(); 554 if (to_node_id == k->GetId() || to_node_id == v->GetId()) { 555 ++weak_entries; 556 } 557 } 558 CHECK_EQ(2, weak_entries); 559 } 560 561 562 TEST(HeapSnapshotInternalReferences) { 563 v8::Isolate* isolate = CcTest::isolate(); 564 v8::HandleScope scope(isolate); 565 v8::Local<v8::ObjectTemplate> global_template = 566 v8::ObjectTemplate::New(isolate); 567 global_template->SetInternalFieldCount(2); 568 LocalContext env(NULL, global_template); 569 v8::Handle<v8::Object> global_proxy = env->Global(); 570 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 571 CHECK_EQ(2, global->InternalFieldCount()); 572 v8::Local<v8::Object> obj = v8::Object::New(isolate); 573 global->SetInternalField(0, v8_num(17)); 574 global->SetInternalField(1, obj); 575 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 576 const v8::HeapSnapshot* snapshot = 577 heap_profiler->TakeHeapSnapshot(v8_str("internals")); 578 CHECK(ValidateSnapshot(snapshot)); 579 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot); 580 // The first reference will not present, because it's a Smi. 581 CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0")); 582 // The second reference is to an object. 583 CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1")); 584 } 585 586 587 // Trying to introduce a check helper for uint32_t causes many 588 // overloading ambiguities, so it seems easier just to cast 589 // them to a signed type. 590 #define CHECK_EQ_SNAPSHOT_OBJECT_ID(a, b) \ 591 CHECK_EQ(static_cast<int32_t>(a), static_cast<int32_t>(b)) 592 #define CHECK_NE_SNAPSHOT_OBJECT_ID(a, b) \ 593 CHECK((a) != (b)) // NOLINT 594 595 TEST(HeapSnapshotAddressReuse) { 596 LocalContext env; 597 v8::HandleScope scope(env->GetIsolate()); 598 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 599 600 CompileRun( 601 "function A() {}\n" 602 "var a = [];\n" 603 "for (var i = 0; i < 10000; ++i)\n" 604 " a[i] = new A();\n"); 605 const v8::HeapSnapshot* snapshot1 = 606 heap_profiler->TakeHeapSnapshot(v8_str("snapshot1")); 607 CHECK(ValidateSnapshot(snapshot1)); 608 v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId(); 609 610 CompileRun( 611 "for (var i = 0; i < 10000; ++i)\n" 612 " a[i] = new A();\n"); 613 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 614 615 const v8::HeapSnapshot* snapshot2 = 616 heap_profiler->TakeHeapSnapshot(v8_str("snapshot2")); 617 CHECK(ValidateSnapshot(snapshot2)); 618 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); 619 620 const v8::HeapGraphNode* array_node = 621 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a"); 622 CHECK_NE(NULL, array_node); 623 int wrong_count = 0; 624 for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) { 625 const v8::HeapGraphEdge* prop = array_node->GetChild(i); 626 if (prop->GetType() != v8::HeapGraphEdge::kElement) 627 continue; 628 v8::SnapshotObjectId id = prop->GetToNode()->GetId(); 629 if (id < maxId1) 630 ++wrong_count; 631 } 632 CHECK_EQ(0, wrong_count); 633 } 634 635 636 TEST(HeapEntryIdsAndArrayShift) { 637 LocalContext env; 638 v8::HandleScope scope(env->GetIsolate()); 639 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 640 641 CompileRun( 642 "function AnObject() {\n" 643 " this.first = 'first';\n" 644 " this.second = 'second';\n" 645 "}\n" 646 "var a = new Array();\n" 647 "for (var i = 0; i < 10; ++i)\n" 648 " a.push(new AnObject());\n"); 649 const v8::HeapSnapshot* snapshot1 = 650 heap_profiler->TakeHeapSnapshot(v8_str("s1")); 651 CHECK(ValidateSnapshot(snapshot1)); 652 653 CompileRun( 654 "for (var i = 0; i < 1; ++i)\n" 655 " a.shift();\n"); 656 657 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 658 659 const v8::HeapSnapshot* snapshot2 = 660 heap_profiler->TakeHeapSnapshot(v8_str("s2")); 661 CHECK(ValidateSnapshot(snapshot2)); 662 663 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1); 664 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); 665 CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId()); 666 CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId()); 667 668 const v8::HeapGraphNode* a1 = 669 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a"); 670 CHECK_NE(NULL, a1); 671 const v8::HeapGraphNode* k1 = 672 GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements"); 673 CHECK_NE(NULL, k1); 674 const v8::HeapGraphNode* a2 = 675 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a"); 676 CHECK_NE(NULL, a2); 677 const v8::HeapGraphNode* k2 = 678 GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements"); 679 CHECK_NE(NULL, k2); 680 681 CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId()); 682 CHECK_EQ_SNAPSHOT_OBJECT_ID(k1->GetId(), k2->GetId()); 683 } 684 685 686 TEST(HeapEntryIdsAndGC) { 687 LocalContext env; 688 v8::HandleScope scope(env->GetIsolate()); 689 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 690 691 CompileRun( 692 "function A() {}\n" 693 "function B(x) { this.x = x; }\n" 694 "var a = new A();\n" 695 "var b = new B(a);"); 696 v8::Local<v8::String> s1_str = v8_str("s1"); 697 v8::Local<v8::String> s2_str = v8_str("s2"); 698 const v8::HeapSnapshot* snapshot1 = 699 heap_profiler->TakeHeapSnapshot(s1_str); 700 CHECK(ValidateSnapshot(snapshot1)); 701 702 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 703 704 const v8::HeapSnapshot* snapshot2 = 705 heap_profiler->TakeHeapSnapshot(s2_str); 706 CHECK(ValidateSnapshot(snapshot2)); 707 708 CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000); 709 CHECK(snapshot1->GetMaxSnapshotJSObjectId() <= 710 snapshot2->GetMaxSnapshotJSObjectId()); 711 712 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1); 713 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); 714 CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId()); 715 CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId()); 716 const v8::HeapGraphNode* A1 = 717 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A"); 718 CHECK_NE(NULL, A1); 719 const v8::HeapGraphNode* A2 = 720 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A"); 721 CHECK_NE(NULL, A2); 722 CHECK_NE_SNAPSHOT_OBJECT_ID(0, A1->GetId()); 723 CHECK_EQ_SNAPSHOT_OBJECT_ID(A1->GetId(), A2->GetId()); 724 const v8::HeapGraphNode* B1 = 725 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B"); 726 CHECK_NE(NULL, B1); 727 const v8::HeapGraphNode* B2 = 728 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B"); 729 CHECK_NE(NULL, B2); 730 CHECK_NE_SNAPSHOT_OBJECT_ID(0, B1->GetId()); 731 CHECK_EQ_SNAPSHOT_OBJECT_ID(B1->GetId(), B2->GetId()); 732 const v8::HeapGraphNode* a1 = 733 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a"); 734 CHECK_NE(NULL, a1); 735 const v8::HeapGraphNode* a2 = 736 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a"); 737 CHECK_NE(NULL, a2); 738 CHECK_NE_SNAPSHOT_OBJECT_ID(0, a1->GetId()); 739 CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId()); 740 const v8::HeapGraphNode* b1 = 741 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b"); 742 CHECK_NE(NULL, b1); 743 const v8::HeapGraphNode* b2 = 744 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b"); 745 CHECK_NE(NULL, b2); 746 CHECK_NE_SNAPSHOT_OBJECT_ID(0, b1->GetId()); 747 CHECK_EQ_SNAPSHOT_OBJECT_ID(b1->GetId(), b2->GetId()); 748 } 749 750 751 TEST(HeapSnapshotRootPreservedAfterSorting) { 752 LocalContext env; 753 v8::HandleScope scope(env->GetIsolate()); 754 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 755 const v8::HeapSnapshot* snapshot = 756 heap_profiler->TakeHeapSnapshot(v8_str("s")); 757 CHECK(ValidateSnapshot(snapshot)); 758 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); 759 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( 760 snapshot))->GetSortedEntriesList(); 761 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); 762 CHECK_EQ(root1, root2); 763 } 764 765 766 namespace { 767 768 class TestJSONStream : public v8::OutputStream { 769 public: 770 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {} 771 explicit TestJSONStream(int abort_countdown) 772 : eos_signaled_(0), abort_countdown_(abort_countdown) {} 773 virtual ~TestJSONStream() {} 774 virtual void EndOfStream() { ++eos_signaled_; } 775 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) { 776 if (abort_countdown_ > 0) --abort_countdown_; 777 if (abort_countdown_ == 0) return kAbort; 778 CHECK_GT(chars_written, 0); 779 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0'); 780 i::MemCopy(chunk.start(), buffer, chars_written); 781 return kContinue; 782 } 783 virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) { 784 ASSERT(false); 785 return kAbort; 786 } 787 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); } 788 int eos_signaled() { return eos_signaled_; } 789 int size() { return buffer_.size(); } 790 791 private: 792 i::Collector<char> buffer_; 793 int eos_signaled_; 794 int abort_countdown_; 795 }; 796 797 class AsciiResource: public v8::String::ExternalAsciiStringResource { 798 public: 799 explicit AsciiResource(i::Vector<char> string): data_(string.start()) { 800 length_ = string.length(); 801 } 802 virtual const char* data() const { return data_; } 803 virtual size_t length() const { return length_; } 804 private: 805 const char* data_; 806 size_t length_; 807 }; 808 809 } // namespace 810 811 TEST(HeapSnapshotJSONSerialization) { 812 LocalContext env; 813 v8::HandleScope scope(env->GetIsolate()); 814 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 815 816 #define STRING_LITERAL_FOR_TEST \ 817 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\"" 818 CompileRun( 819 "function A(s) { this.s = s; }\n" 820 "function B(x) { this.x = x; }\n" 821 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n" 822 "var b = new B(a);"); 823 const v8::HeapSnapshot* snapshot = 824 heap_profiler->TakeHeapSnapshot(v8_str("json")); 825 CHECK(ValidateSnapshot(snapshot)); 826 827 TestJSONStream stream; 828 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); 829 CHECK_GT(stream.size(), 0); 830 CHECK_EQ(1, stream.eos_signaled()); 831 i::ScopedVector<char> json(stream.size()); 832 stream.WriteTo(json); 833 834 // Verify that snapshot string is valid JSON. 835 AsciiResource* json_res = new AsciiResource(json); 836 v8::Local<v8::String> json_string = 837 v8::String::NewExternal(env->GetIsolate(), json_res); 838 env->Global()->Set(v8_str("json_snapshot"), json_string); 839 v8::Local<v8::Value> snapshot_parse_result = CompileRun( 840 "var parsed = JSON.parse(json_snapshot); true;"); 841 CHECK(!snapshot_parse_result.IsEmpty()); 842 843 // Verify that snapshot object has required fields. 844 v8::Local<v8::Object> parsed_snapshot = 845 env->Global()->Get(v8_str("parsed"))->ToObject(); 846 CHECK(parsed_snapshot->Has(v8_str("snapshot"))); 847 CHECK(parsed_snapshot->Has(v8_str("nodes"))); 848 CHECK(parsed_snapshot->Has(v8_str("edges"))); 849 CHECK(parsed_snapshot->Has(v8_str("strings"))); 850 851 // Get node and edge "member" offsets. 852 v8::Local<v8::Value> meta_analysis_result = CompileRun( 853 "var meta = parsed.snapshot.meta;\n" 854 "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n" 855 "var node_fields_count = meta.node_fields.length;\n" 856 "var edge_fields_count = meta.edge_fields.length;\n" 857 "var edge_type_offset = meta.edge_fields.indexOf('type');\n" 858 "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n" 859 "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n" 860 "var property_type =" 861 " meta.edge_types[edge_type_offset].indexOf('property');\n" 862 "var shortcut_type =" 863 " meta.edge_types[edge_type_offset].indexOf('shortcut');\n" 864 "var node_count = parsed.nodes.length / node_fields_count;\n" 865 "var first_edge_indexes = parsed.first_edge_indexes = [];\n" 866 "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n" 867 " first_edge_indexes[i] = first_edge_index;\n" 868 " first_edge_index += edge_fields_count *\n" 869 " parsed.nodes[i * node_fields_count + edge_count_offset];\n" 870 "}\n" 871 "first_edge_indexes[node_count] = first_edge_index;\n"); 872 CHECK(!meta_analysis_result.IsEmpty()); 873 874 // A helper function for processing encoded nodes. 875 CompileRun( 876 "function GetChildPosByProperty(pos, prop_name, prop_type) {\n" 877 " var nodes = parsed.nodes;\n" 878 " var edges = parsed.edges;\n" 879 " var strings = parsed.strings;\n" 880 " var node_ordinal = pos / node_fields_count;\n" 881 " for (var i = parsed.first_edge_indexes[node_ordinal],\n" 882 " count = parsed.first_edge_indexes[node_ordinal + 1];\n" 883 " i < count; i += edge_fields_count) {\n" 884 " if (edges[i + edge_type_offset] === prop_type\n" 885 " && strings[edges[i + edge_name_offset]] === prop_name)\n" 886 " return edges[i + edge_to_node_offset];\n" 887 " }\n" 888 " return null;\n" 889 "}\n"); 890 // Get the string index using the path: <root> -> <global>.b.x.s 891 v8::Local<v8::Value> string_obj_pos_val = CompileRun( 892 "GetChildPosByProperty(\n" 893 " GetChildPosByProperty(\n" 894 " GetChildPosByProperty(" 895 " parsed.edges[edge_fields_count + edge_to_node_offset]," 896 " \"b\", property_type),\n" 897 " \"x\", property_type)," 898 " \"s\", property_type)"); 899 CHECK(!string_obj_pos_val.IsEmpty()); 900 int string_obj_pos = 901 static_cast<int>(string_obj_pos_val->ToNumber()->Value()); 902 v8::Local<v8::Object> nodes_array = 903 parsed_snapshot->Get(v8_str("nodes"))->ToObject(); 904 int string_index = static_cast<int>( 905 nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value()); 906 CHECK_GT(string_index, 0); 907 v8::Local<v8::Object> strings_array = 908 parsed_snapshot->Get(v8_str("strings"))->ToObject(); 909 v8::Local<v8::String> string = strings_array->Get(string_index)->ToString(); 910 v8::Local<v8::String> ref_string = 911 CompileRun(STRING_LITERAL_FOR_TEST)->ToString(); 912 #undef STRING_LITERAL_FOR_TEST 913 CHECK_EQ(*v8::String::Utf8Value(ref_string), 914 *v8::String::Utf8Value(string)); 915 } 916 917 918 TEST(HeapSnapshotJSONSerializationAborting) { 919 LocalContext env; 920 v8::HandleScope scope(env->GetIsolate()); 921 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 922 const v8::HeapSnapshot* snapshot = 923 heap_profiler->TakeHeapSnapshot(v8_str("abort")); 924 CHECK(ValidateSnapshot(snapshot)); 925 TestJSONStream stream(5); 926 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); 927 CHECK_GT(stream.size(), 0); 928 CHECK_EQ(0, stream.eos_signaled()); 929 } 930 931 namespace { 932 933 class TestStatsStream : public v8::OutputStream { 934 public: 935 TestStatsStream() 936 : eos_signaled_(0), 937 updates_written_(0), 938 entries_count_(0), 939 entries_size_(0), 940 intervals_count_(0), 941 first_interval_index_(-1) { } 942 TestStatsStream(const TestStatsStream& stream) 943 : v8::OutputStream(stream), 944 eos_signaled_(stream.eos_signaled_), 945 updates_written_(stream.updates_written_), 946 entries_count_(stream.entries_count_), 947 entries_size_(stream.entries_size_), 948 intervals_count_(stream.intervals_count_), 949 first_interval_index_(stream.first_interval_index_) { } 950 virtual ~TestStatsStream() {} 951 virtual void EndOfStream() { ++eos_signaled_; } 952 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) { 953 ASSERT(false); 954 return kAbort; 955 } 956 virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer, 957 int updates_written) { 958 ++intervals_count_; 959 ASSERT(updates_written); 960 updates_written_ += updates_written; 961 entries_count_ = 0; 962 if (first_interval_index_ == -1 && updates_written != 0) 963 first_interval_index_ = buffer[0].index; 964 for (int i = 0; i < updates_written; ++i) { 965 entries_count_ += buffer[i].count; 966 entries_size_ += buffer[i].size; 967 } 968 969 return kContinue; 970 } 971 int eos_signaled() { return eos_signaled_; } 972 int updates_written() { return updates_written_; } 973 uint32_t entries_count() const { return entries_count_; } 974 uint32_t entries_size() const { return entries_size_; } 975 int intervals_count() const { return intervals_count_; } 976 int first_interval_index() const { return first_interval_index_; } 977 978 private: 979 int eos_signaled_; 980 int updates_written_; 981 uint32_t entries_count_; 982 uint32_t entries_size_; 983 int intervals_count_; 984 int first_interval_index_; 985 }; 986 987 } // namespace 988 989 static TestStatsStream GetHeapStatsUpdate( 990 v8::HeapProfiler* heap_profiler, 991 v8::SnapshotObjectId* object_id = NULL) { 992 TestStatsStream stream; 993 v8::SnapshotObjectId last_seen_id = heap_profiler->GetHeapStats(&stream); 994 if (object_id) 995 *object_id = last_seen_id; 996 CHECK_EQ(1, stream.eos_signaled()); 997 return stream; 998 } 999 1000 1001 TEST(HeapSnapshotObjectsStats) { 1002 LocalContext env; 1003 v8::HandleScope scope(env->GetIsolate()); 1004 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1005 1006 heap_profiler->StartTrackingHeapObjects(); 1007 // We have to call GC 6 times. In other case the garbage will be 1008 // the reason of flakiness. 1009 for (int i = 0; i < 6; ++i) { 1010 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 1011 } 1012 1013 v8::SnapshotObjectId initial_id; 1014 { 1015 // Single chunk of data expected in update. Initial data. 1016 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler, 1017 &initial_id); 1018 CHECK_EQ(1, stats_update.intervals_count()); 1019 CHECK_EQ(1, stats_update.updates_written()); 1020 CHECK_LT(0, stats_update.entries_size()); 1021 CHECK_EQ(0, stats_update.first_interval_index()); 1022 } 1023 1024 // No data expected in update because nothing has happened. 1025 v8::SnapshotObjectId same_id; 1026 CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written()); 1027 CHECK_EQ_SNAPSHOT_OBJECT_ID(initial_id, same_id); 1028 1029 { 1030 v8::SnapshotObjectId additional_string_id; 1031 v8::HandleScope inner_scope_1(env->GetIsolate()); 1032 v8_str("string1"); 1033 { 1034 // Single chunk of data with one new entry expected in update. 1035 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler, 1036 &additional_string_id); 1037 CHECK_LT(same_id, additional_string_id); 1038 CHECK_EQ(1, stats_update.intervals_count()); 1039 CHECK_EQ(1, stats_update.updates_written()); 1040 CHECK_LT(0, stats_update.entries_size()); 1041 CHECK_EQ(1, stats_update.entries_count()); 1042 CHECK_EQ(2, stats_update.first_interval_index()); 1043 } 1044 1045 // No data expected in update because nothing happened. 1046 v8::SnapshotObjectId last_id; 1047 CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written()); 1048 CHECK_EQ_SNAPSHOT_OBJECT_ID(additional_string_id, last_id); 1049 1050 { 1051 v8::HandleScope inner_scope_2(env->GetIsolate()); 1052 v8_str("string2"); 1053 1054 uint32_t entries_size; 1055 { 1056 v8::HandleScope inner_scope_3(env->GetIsolate()); 1057 v8_str("string3"); 1058 v8_str("string4"); 1059 1060 { 1061 // Single chunk of data with three new entries expected in update. 1062 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1063 CHECK_EQ(1, stats_update.intervals_count()); 1064 CHECK_EQ(1, stats_update.updates_written()); 1065 CHECK_LT(0, entries_size = stats_update.entries_size()); 1066 CHECK_EQ(3, stats_update.entries_count()); 1067 CHECK_EQ(4, stats_update.first_interval_index()); 1068 } 1069 } 1070 1071 { 1072 // Single chunk of data with two left entries expected in update. 1073 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1074 CHECK_EQ(1, stats_update.intervals_count()); 1075 CHECK_EQ(1, stats_update.updates_written()); 1076 CHECK_GT(entries_size, stats_update.entries_size()); 1077 CHECK_EQ(1, stats_update.entries_count()); 1078 // Two strings from forth interval were released. 1079 CHECK_EQ(4, stats_update.first_interval_index()); 1080 } 1081 } 1082 1083 { 1084 // Single chunk of data with 0 left entries expected in update. 1085 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1086 CHECK_EQ(1, stats_update.intervals_count()); 1087 CHECK_EQ(1, stats_update.updates_written()); 1088 CHECK_EQ(0, stats_update.entries_size()); 1089 CHECK_EQ(0, stats_update.entries_count()); 1090 // The last string from forth interval was released. 1091 CHECK_EQ(4, stats_update.first_interval_index()); 1092 } 1093 } 1094 { 1095 // Single chunk of data with 0 left entries expected in update. 1096 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1097 CHECK_EQ(1, stats_update.intervals_count()); 1098 CHECK_EQ(1, stats_update.updates_written()); 1099 CHECK_EQ(0, stats_update.entries_size()); 1100 CHECK_EQ(0, stats_update.entries_count()); 1101 // The only string from the second interval was released. 1102 CHECK_EQ(2, stats_update.first_interval_index()); 1103 } 1104 1105 v8::Local<v8::Array> array = v8::Array::New(env->GetIsolate()); 1106 CHECK_EQ(0, array->Length()); 1107 // Force array's buffer allocation. 1108 array->Set(2, v8_num(7)); 1109 1110 uint32_t entries_size; 1111 { 1112 // Single chunk of data with 2 entries expected in update. 1113 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1114 CHECK_EQ(1, stats_update.intervals_count()); 1115 CHECK_EQ(1, stats_update.updates_written()); 1116 CHECK_LT(0, entries_size = stats_update.entries_size()); 1117 // They are the array and its buffer. 1118 CHECK_EQ(2, stats_update.entries_count()); 1119 CHECK_EQ(8, stats_update.first_interval_index()); 1120 } 1121 1122 for (int i = 0; i < 100; ++i) 1123 array->Set(i, v8_num(i)); 1124 1125 { 1126 // Single chunk of data with 1 entry expected in update. 1127 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1128 CHECK_EQ(1, stats_update.intervals_count()); 1129 // The first interval was changed because old buffer was collected. 1130 // The second interval was changed because new buffer was allocated. 1131 CHECK_EQ(2, stats_update.updates_written()); 1132 CHECK_LT(entries_size, stats_update.entries_size()); 1133 CHECK_EQ(2, stats_update.entries_count()); 1134 CHECK_EQ(8, stats_update.first_interval_index()); 1135 } 1136 1137 heap_profiler->StopTrackingHeapObjects(); 1138 } 1139 1140 1141 TEST(HeapObjectIds) { 1142 LocalContext env; 1143 v8::Isolate* isolate = env->GetIsolate(); 1144 v8::HandleScope scope(isolate); 1145 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1146 1147 const int kLength = 10; 1148 v8::Handle<v8::Object> objects[kLength]; 1149 v8::SnapshotObjectId ids[kLength]; 1150 1151 heap_profiler->StartTrackingHeapObjects(false); 1152 1153 for (int i = 0; i < kLength; i++) { 1154 objects[i] = v8::Object::New(isolate); 1155 } 1156 GetHeapStatsUpdate(heap_profiler); 1157 1158 for (int i = 0; i < kLength; i++) { 1159 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]); 1160 CHECK_NE(v8::HeapProfiler::kUnknownObjectId, static_cast<int>(id)); 1161 ids[i] = id; 1162 } 1163 1164 heap_profiler->StopTrackingHeapObjects(); 1165 CcTest::heap()->CollectAllAvailableGarbage(); 1166 1167 for (int i = 0; i < kLength; i++) { 1168 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]); 1169 CHECK_EQ(static_cast<int>(ids[i]), static_cast<int>(id)); 1170 v8::Handle<v8::Value> obj = heap_profiler->FindObjectById(ids[i]); 1171 CHECK_EQ(objects[i], obj); 1172 } 1173 1174 heap_profiler->ClearObjectIds(); 1175 for (int i = 0; i < kLength; i++) { 1176 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]); 1177 CHECK_EQ(v8::HeapProfiler::kUnknownObjectId, static_cast<int>(id)); 1178 v8::Handle<v8::Value> obj = heap_profiler->FindObjectById(ids[i]); 1179 CHECK(obj.IsEmpty()); 1180 } 1181 } 1182 1183 1184 static void CheckChildrenIds(const v8::HeapSnapshot* snapshot, 1185 const v8::HeapGraphNode* node, 1186 int level, int max_level) { 1187 if (level > max_level) return; 1188 CHECK_EQ(node, snapshot->GetNodeById(node->GetId())); 1189 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { 1190 const v8::HeapGraphEdge* prop = node->GetChild(i); 1191 const v8::HeapGraphNode* child = 1192 snapshot->GetNodeById(prop->GetToNode()->GetId()); 1193 CHECK_EQ_SNAPSHOT_OBJECT_ID(prop->GetToNode()->GetId(), child->GetId()); 1194 CHECK_EQ(prop->GetToNode(), child); 1195 CheckChildrenIds(snapshot, child, level + 1, max_level); 1196 } 1197 } 1198 1199 1200 TEST(HeapSnapshotGetNodeById) { 1201 LocalContext env; 1202 v8::HandleScope scope(env->GetIsolate()); 1203 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1204 1205 const v8::HeapSnapshot* snapshot = 1206 heap_profiler->TakeHeapSnapshot(v8_str("id")); 1207 CHECK(ValidateSnapshot(snapshot)); 1208 const v8::HeapGraphNode* root = snapshot->GetRoot(); 1209 CheckChildrenIds(snapshot, root, 0, 3); 1210 // Check a big id, which should not exist yet. 1211 CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL)); 1212 } 1213 1214 1215 TEST(HeapSnapshotGetSnapshotObjectId) { 1216 LocalContext env; 1217 v8::HandleScope scope(env->GetIsolate()); 1218 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1219 CompileRun("globalObject = {};\n"); 1220 const v8::HeapSnapshot* snapshot = 1221 heap_profiler->TakeHeapSnapshot(v8_str("get_snapshot_object_id")); 1222 CHECK(ValidateSnapshot(snapshot)); 1223 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1224 const v8::HeapGraphNode* global_object = 1225 GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject"); 1226 CHECK(global_object); 1227 1228 v8::Local<v8::Value> globalObjectHandle = env->Global()->Get( 1229 v8::String::NewFromUtf8(env->GetIsolate(), "globalObject")); 1230 CHECK(!globalObjectHandle.IsEmpty()); 1231 CHECK(globalObjectHandle->IsObject()); 1232 1233 v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle); 1234 CHECK_NE(static_cast<int>(v8::HeapProfiler::kUnknownObjectId), 1235 id); 1236 CHECK_EQ(static_cast<int>(id), global_object->GetId()); 1237 } 1238 1239 1240 TEST(HeapSnapshotUnknownSnapshotObjectId) { 1241 LocalContext env; 1242 v8::HandleScope scope(env->GetIsolate()); 1243 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1244 CompileRun("globalObject = {};\n"); 1245 const v8::HeapSnapshot* snapshot = 1246 heap_profiler->TakeHeapSnapshot(v8_str("unknown_object_id")); 1247 CHECK(ValidateSnapshot(snapshot)); 1248 const v8::HeapGraphNode* node = 1249 snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId); 1250 CHECK_EQ(NULL, node); 1251 } 1252 1253 1254 namespace { 1255 1256 class TestActivityControl : public v8::ActivityControl { 1257 public: 1258 explicit TestActivityControl(int abort_count) 1259 : done_(0), total_(0), abort_count_(abort_count) {} 1260 ControlOption ReportProgressValue(int done, int total) { 1261 done_ = done; 1262 total_ = total; 1263 return --abort_count_ != 0 ? kContinue : kAbort; 1264 } 1265 int done() { return done_; } 1266 int total() { return total_; } 1267 1268 private: 1269 int done_; 1270 int total_; 1271 int abort_count_; 1272 }; 1273 } 1274 1275 1276 TEST(TakeHeapSnapshotAborting) { 1277 LocalContext env; 1278 v8::HandleScope scope(env->GetIsolate()); 1279 1280 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1281 const int snapshots_count = heap_profiler->GetSnapshotCount(); 1282 TestActivityControl aborting_control(1); 1283 const v8::HeapSnapshot* no_snapshot = 1284 heap_profiler->TakeHeapSnapshot(v8_str("abort"), 1285 &aborting_control); 1286 CHECK_EQ(NULL, no_snapshot); 1287 CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount()); 1288 CHECK_GT(aborting_control.total(), aborting_control.done()); 1289 1290 TestActivityControl control(-1); // Don't abort. 1291 const v8::HeapSnapshot* snapshot = 1292 heap_profiler->TakeHeapSnapshot(v8_str("full"), 1293 &control); 1294 CHECK(ValidateSnapshot(snapshot)); 1295 1296 CHECK_NE(NULL, snapshot); 1297 CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount()); 1298 CHECK_EQ(control.total(), control.done()); 1299 CHECK_GT(control.total(), 0); 1300 } 1301 1302 1303 namespace { 1304 1305 class TestRetainedObjectInfo : public v8::RetainedObjectInfo { 1306 public: 1307 TestRetainedObjectInfo(int hash, 1308 const char* group_label, 1309 const char* label, 1310 intptr_t element_count = -1, 1311 intptr_t size = -1) 1312 : disposed_(false), 1313 hash_(hash), 1314 group_label_(group_label), 1315 label_(label), 1316 element_count_(element_count), 1317 size_(size) { 1318 instances.Add(this); 1319 } 1320 virtual ~TestRetainedObjectInfo() {} 1321 virtual void Dispose() { 1322 CHECK(!disposed_); 1323 disposed_ = true; 1324 } 1325 virtual bool IsEquivalent(RetainedObjectInfo* other) { 1326 return GetHash() == other->GetHash(); 1327 } 1328 virtual intptr_t GetHash() { return hash_; } 1329 virtual const char* GetGroupLabel() { return group_label_; } 1330 virtual const char* GetLabel() { return label_; } 1331 virtual intptr_t GetElementCount() { return element_count_; } 1332 virtual intptr_t GetSizeInBytes() { return size_; } 1333 bool disposed() { return disposed_; } 1334 1335 static v8::RetainedObjectInfo* WrapperInfoCallback( 1336 uint16_t class_id, v8::Handle<v8::Value> wrapper) { 1337 if (class_id == 1) { 1338 if (wrapper->IsString()) { 1339 v8::String::Utf8Value utf8(wrapper); 1340 if (strcmp(*utf8, "AAA") == 0) 1341 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100); 1342 else if (strcmp(*utf8, "BBB") == 0) 1343 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100); 1344 } 1345 } else if (class_id == 2) { 1346 if (wrapper->IsString()) { 1347 v8::String::Utf8Value utf8(wrapper); 1348 if (strcmp(*utf8, "CCC") == 0) 1349 return new TestRetainedObjectInfo(2, "ccc-group", "ccc"); 1350 } 1351 } 1352 CHECK(false); 1353 return NULL; 1354 } 1355 1356 static i::List<TestRetainedObjectInfo*> instances; 1357 1358 private: 1359 bool disposed_; 1360 int hash_; 1361 const char* group_label_; 1362 const char* label_; 1363 intptr_t element_count_; 1364 intptr_t size_; 1365 }; 1366 1367 1368 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances; 1369 } 1370 1371 1372 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent, 1373 v8::HeapGraphNode::Type type, 1374 const char* name) { 1375 for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) { 1376 const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode(); 1377 if (node->GetType() == type && strcmp(name, 1378 const_cast<i::HeapEntry*>( 1379 reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) { 1380 return node; 1381 } 1382 } 1383 return NULL; 1384 } 1385 1386 1387 TEST(HeapSnapshotRetainedObjectInfo) { 1388 LocalContext env; 1389 v8::Isolate* isolate = env->GetIsolate(); 1390 v8::HandleScope scope(isolate); 1391 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 1392 1393 heap_profiler->SetWrapperClassInfoProvider( 1394 1, TestRetainedObjectInfo::WrapperInfoCallback); 1395 heap_profiler->SetWrapperClassInfoProvider( 1396 2, TestRetainedObjectInfo::WrapperInfoCallback); 1397 v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA")); 1398 p_AAA.SetWrapperClassId(1); 1399 v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB")); 1400 p_BBB.SetWrapperClassId(1); 1401 v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC")); 1402 p_CCC.SetWrapperClassId(2); 1403 CHECK_EQ(0, TestRetainedObjectInfo::instances.length()); 1404 const v8::HeapSnapshot* snapshot = 1405 heap_profiler->TakeHeapSnapshot(v8_str("retained")); 1406 CHECK(ValidateSnapshot(snapshot)); 1407 1408 CHECK_EQ(3, TestRetainedObjectInfo::instances.length()); 1409 for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) { 1410 CHECK(TestRetainedObjectInfo::instances[i]->disposed()); 1411 delete TestRetainedObjectInfo::instances[i]; 1412 } 1413 1414 const v8::HeapGraphNode* native_group_aaa = GetNode( 1415 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group"); 1416 CHECK_NE(NULL, native_group_aaa); 1417 CHECK_EQ(1, native_group_aaa->GetChildrenCount()); 1418 const v8::HeapGraphNode* aaa = GetNode( 1419 native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries"); 1420 CHECK_NE(NULL, aaa); 1421 CHECK_EQ(2, aaa->GetChildrenCount()); 1422 1423 const v8::HeapGraphNode* native_group_ccc = GetNode( 1424 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group"); 1425 const v8::HeapGraphNode* ccc = GetNode( 1426 native_group_ccc, v8::HeapGraphNode::kNative, "ccc"); 1427 CHECK_NE(NULL, ccc); 1428 1429 const v8::HeapGraphNode* n_AAA = GetNode( 1430 aaa, v8::HeapGraphNode::kString, "AAA"); 1431 CHECK_NE(NULL, n_AAA); 1432 const v8::HeapGraphNode* n_BBB = GetNode( 1433 aaa, v8::HeapGraphNode::kString, "BBB"); 1434 CHECK_NE(NULL, n_BBB); 1435 CHECK_EQ(1, ccc->GetChildrenCount()); 1436 const v8::HeapGraphNode* n_CCC = GetNode( 1437 ccc, v8::HeapGraphNode::kString, "CCC"); 1438 CHECK_NE(NULL, n_CCC); 1439 1440 CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native")); 1441 CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native")); 1442 CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native")); 1443 } 1444 1445 1446 class GraphWithImplicitRefs { 1447 public: 1448 static const int kObjectsCount = 4; 1449 explicit GraphWithImplicitRefs(LocalContext* env) { 1450 CHECK_EQ(NULL, instance_); 1451 instance_ = this; 1452 isolate_ = (*env)->GetIsolate(); 1453 for (int i = 0; i < kObjectsCount; i++) { 1454 objects_[i].Reset(isolate_, v8::Object::New(isolate_)); 1455 } 1456 (*env)->Global()->Set(v8_str("root_object"), 1457 v8::Local<v8::Value>::New(isolate_, objects_[0])); 1458 } 1459 ~GraphWithImplicitRefs() { 1460 instance_ = NULL; 1461 } 1462 1463 static void gcPrologue(v8::GCType type, v8::GCCallbackFlags flags) { 1464 instance_->AddImplicitReferences(); 1465 } 1466 1467 private: 1468 void AddImplicitReferences() { 1469 // 0 -> 1 1470 isolate_->SetObjectGroupId(objects_[0], 1471 v8::UniqueId(1)); 1472 isolate_->SetReferenceFromGroup( 1473 v8::UniqueId(1), objects_[1]); 1474 // Adding two more references: 1 -> 2, 1 -> 3 1475 isolate_->SetReference(objects_[1].As<v8::Object>(), 1476 objects_[2]); 1477 isolate_->SetReference(objects_[1].As<v8::Object>(), 1478 objects_[3]); 1479 } 1480 1481 v8::Persistent<v8::Value> objects_[kObjectsCount]; 1482 static GraphWithImplicitRefs* instance_; 1483 v8::Isolate* isolate_; 1484 }; 1485 1486 GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL; 1487 1488 1489 TEST(HeapSnapshotImplicitReferences) { 1490 LocalContext env; 1491 v8::HandleScope scope(env->GetIsolate()); 1492 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1493 1494 GraphWithImplicitRefs graph(&env); 1495 v8::V8::AddGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue); 1496 1497 const v8::HeapSnapshot* snapshot = 1498 heap_profiler->TakeHeapSnapshot(v8_str("implicit_refs")); 1499 CHECK(ValidateSnapshot(snapshot)); 1500 1501 const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot); 1502 const v8::HeapGraphNode* obj0 = GetProperty( 1503 global_object, v8::HeapGraphEdge::kProperty, "root_object"); 1504 CHECK(obj0); 1505 CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType()); 1506 const v8::HeapGraphNode* obj1 = GetProperty( 1507 obj0, v8::HeapGraphEdge::kInternal, "native"); 1508 CHECK(obj1); 1509 int implicit_targets_count = 0; 1510 for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) { 1511 const v8::HeapGraphEdge* prop = obj1->GetChild(i); 1512 v8::String::Utf8Value prop_name(prop->GetName()); 1513 if (prop->GetType() == v8::HeapGraphEdge::kInternal && 1514 strcmp("native", *prop_name) == 0) { 1515 ++implicit_targets_count; 1516 } 1517 } 1518 CHECK_EQ(2, implicit_targets_count); 1519 v8::V8::RemoveGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue); 1520 } 1521 1522 1523 TEST(DeleteAllHeapSnapshots) { 1524 LocalContext env; 1525 v8::HandleScope scope(env->GetIsolate()); 1526 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1527 1528 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1529 heap_profiler->DeleteAllHeapSnapshots(); 1530 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1531 CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("1"))); 1532 CHECK_EQ(1, heap_profiler->GetSnapshotCount()); 1533 heap_profiler->DeleteAllHeapSnapshots(); 1534 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1535 CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("1"))); 1536 CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("2"))); 1537 CHECK_EQ(2, heap_profiler->GetSnapshotCount()); 1538 heap_profiler->DeleteAllHeapSnapshots(); 1539 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1540 } 1541 1542 1543 static const v8::HeapSnapshot* FindHeapSnapshot(v8::HeapProfiler* profiler, 1544 unsigned uid) { 1545 int length = profiler->GetSnapshotCount(); 1546 for (int i = 0; i < length; i++) { 1547 const v8::HeapSnapshot* snapshot = profiler->GetHeapSnapshot(i); 1548 if (snapshot->GetUid() == uid) { 1549 return snapshot; 1550 } 1551 } 1552 return NULL; 1553 } 1554 1555 1556 TEST(DeleteHeapSnapshot) { 1557 LocalContext env; 1558 v8::HandleScope scope(env->GetIsolate()); 1559 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1560 1561 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1562 const v8::HeapSnapshot* s1 = 1563 heap_profiler->TakeHeapSnapshot(v8_str("1")); 1564 1565 CHECK_NE(NULL, s1); 1566 CHECK_EQ(1, heap_profiler->GetSnapshotCount()); 1567 unsigned uid1 = s1->GetUid(); 1568 CHECK_EQ(s1, FindHeapSnapshot(heap_profiler, uid1)); 1569 const_cast<v8::HeapSnapshot*>(s1)->Delete(); 1570 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1571 CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid1)); 1572 1573 const v8::HeapSnapshot* s2 = 1574 heap_profiler->TakeHeapSnapshot(v8_str("2")); 1575 CHECK_NE(NULL, s2); 1576 CHECK_EQ(1, heap_profiler->GetSnapshotCount()); 1577 unsigned uid2 = s2->GetUid(); 1578 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2)); 1579 CHECK_EQ(s2, FindHeapSnapshot(heap_profiler, uid2)); 1580 const v8::HeapSnapshot* s3 = 1581 heap_profiler->TakeHeapSnapshot(v8_str("3")); 1582 CHECK_NE(NULL, s3); 1583 CHECK_EQ(2, heap_profiler->GetSnapshotCount()); 1584 unsigned uid3 = s3->GetUid(); 1585 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3)); 1586 CHECK_EQ(s3, FindHeapSnapshot(heap_profiler, uid3)); 1587 const_cast<v8::HeapSnapshot*>(s2)->Delete(); 1588 CHECK_EQ(1, heap_profiler->GetSnapshotCount()); 1589 CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid2)); 1590 CHECK_EQ(s3, FindHeapSnapshot(heap_profiler, uid3)); 1591 const_cast<v8::HeapSnapshot*>(s3)->Delete(); 1592 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1593 CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid3)); 1594 } 1595 1596 1597 class NameResolver : public v8::HeapProfiler::ObjectNameResolver { 1598 public: 1599 virtual const char* GetName(v8::Handle<v8::Object> object) { 1600 return "Global object name"; 1601 } 1602 }; 1603 1604 1605 TEST(GlobalObjectName) { 1606 LocalContext env; 1607 v8::HandleScope scope(env->GetIsolate()); 1608 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1609 1610 CompileRun("document = { URL:\"abcdefgh\" };"); 1611 1612 NameResolver name_resolver; 1613 const v8::HeapSnapshot* snapshot = 1614 heap_profiler->TakeHeapSnapshot(v8_str("document"), 1615 NULL, 1616 &name_resolver); 1617 CHECK(ValidateSnapshot(snapshot)); 1618 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1619 CHECK_NE(NULL, global); 1620 CHECK_EQ("Object / Global object name" , 1621 const_cast<i::HeapEntry*>( 1622 reinterpret_cast<const i::HeapEntry*>(global))->name()); 1623 } 1624 1625 1626 TEST(GlobalObjectFields) { 1627 LocalContext env; 1628 v8::HandleScope scope(env->GetIsolate()); 1629 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1630 CompileRun("obj = {};"); 1631 const v8::HeapSnapshot* snapshot = 1632 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 1633 CHECK(ValidateSnapshot(snapshot)); 1634 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1635 const v8::HeapGraphNode* builtins = 1636 GetProperty(global, v8::HeapGraphEdge::kInternal, "builtins"); 1637 CHECK_NE(NULL, builtins); 1638 const v8::HeapGraphNode* native_context = 1639 GetProperty(global, v8::HeapGraphEdge::kInternal, "native_context"); 1640 CHECK_NE(NULL, native_context); 1641 const v8::HeapGraphNode* global_context = 1642 GetProperty(global, v8::HeapGraphEdge::kInternal, "global_context"); 1643 CHECK_NE(NULL, global_context); 1644 const v8::HeapGraphNode* global_receiver = 1645 GetProperty(global, v8::HeapGraphEdge::kInternal, "global_receiver"); 1646 CHECK_NE(NULL, global_receiver); 1647 } 1648 1649 1650 TEST(NoHandleLeaks) { 1651 LocalContext env; 1652 v8::HandleScope scope(env->GetIsolate()); 1653 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1654 1655 CompileRun("document = { URL:\"abcdefgh\" };"); 1656 1657 v8::Handle<v8::String> name(v8_str("leakz")); 1658 i::Isolate* isolate = CcTest::i_isolate(); 1659 int count_before = i::HandleScope::NumberOfHandles(isolate); 1660 heap_profiler->TakeHeapSnapshot(name); 1661 int count_after = i::HandleScope::NumberOfHandles(isolate); 1662 CHECK_EQ(count_before, count_after); 1663 } 1664 1665 1666 TEST(NodesIteration) { 1667 LocalContext env; 1668 v8::HandleScope scope(env->GetIsolate()); 1669 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1670 const v8::HeapSnapshot* snapshot = 1671 heap_profiler->TakeHeapSnapshot(v8_str("iteration")); 1672 CHECK(ValidateSnapshot(snapshot)); 1673 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1674 CHECK_NE(NULL, global); 1675 // Verify that we can find this object by iteration. 1676 const int nodes_count = snapshot->GetNodesCount(); 1677 int count = 0; 1678 for (int i = 0; i < nodes_count; ++i) { 1679 if (snapshot->GetNode(i) == global) 1680 ++count; 1681 } 1682 CHECK_EQ(1, count); 1683 } 1684 1685 1686 TEST(GetHeapValueForNode) { 1687 LocalContext env; 1688 v8::HandleScope scope(env->GetIsolate()); 1689 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1690 1691 CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };"); 1692 const v8::HeapSnapshot* snapshot = 1693 heap_profiler->TakeHeapSnapshot(v8_str("value")); 1694 CHECK(ValidateSnapshot(snapshot)); 1695 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1696 CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject()); 1697 v8::Local<v8::Object> js_global = 1698 env->Global()->GetPrototype().As<v8::Object>(); 1699 CHECK(js_global == heap_profiler->FindObjectById(global->GetId())); 1700 const v8::HeapGraphNode* obj = GetProperty( 1701 global, v8::HeapGraphEdge::kProperty, "a"); 1702 CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject()); 1703 v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>(); 1704 CHECK(js_obj == heap_profiler->FindObjectById(obj->GetId())); 1705 const v8::HeapGraphNode* s_prop = 1706 GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop"); 1707 v8::Local<v8::String> js_s_prop = 1708 js_obj->Get(v8_str("s_prop")).As<v8::String>(); 1709 CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId())); 1710 const v8::HeapGraphNode* n_prop = 1711 GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop"); 1712 v8::Local<v8::Number> js_n_prop = 1713 js_obj->Get(v8_str("n_prop")).As<v8::Number>(); 1714 CHECK(js_n_prop->NumberValue() == 1715 heap_profiler->FindObjectById(n_prop->GetId())->NumberValue()); 1716 } 1717 1718 1719 TEST(GetHeapValueForDeletedObject) { 1720 LocalContext env; 1721 v8::HandleScope scope(env->GetIsolate()); 1722 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1723 1724 // It is impossible to delete a global property, so we are about to delete a 1725 // property of the "a" object. Also, the "p" object can't be an empty one 1726 // because the empty object is static and isn't actually deleted. 1727 CompileRun("a = { p: { r: {} } };"); 1728 const v8::HeapSnapshot* snapshot = 1729 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 1730 CHECK(ValidateSnapshot(snapshot)); 1731 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1732 const v8::HeapGraphNode* obj = GetProperty( 1733 global, v8::HeapGraphEdge::kProperty, "a"); 1734 const v8::HeapGraphNode* prop = GetProperty( 1735 obj, v8::HeapGraphEdge::kProperty, "p"); 1736 { 1737 // Perform the check inside a nested local scope to avoid creating a 1738 // reference to the object we are deleting. 1739 v8::HandleScope scope(env->GetIsolate()); 1740 CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject()); 1741 } 1742 CompileRun("delete a.p;"); 1743 CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty()); 1744 } 1745 1746 1747 static int StringCmp(const char* ref, i::String* act) { 1748 i::SmartArrayPointer<char> s_act = act->ToCString(); 1749 int result = strcmp(ref, s_act.get()); 1750 if (result != 0) 1751 fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get()); 1752 return result; 1753 } 1754 1755 1756 TEST(GetConstructorName) { 1757 LocalContext env; 1758 v8::HandleScope scope(env->GetIsolate()); 1759 1760 CompileRun( 1761 "function Constructor1() {};\n" 1762 "var obj1 = new Constructor1();\n" 1763 "var Constructor2 = function() {};\n" 1764 "var obj2 = new Constructor2();\n" 1765 "var obj3 = {};\n" 1766 "obj3.constructor = function Constructor3() {};\n" 1767 "var obj4 = {};\n" 1768 "// Slow properties\n" 1769 "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n" 1770 "obj4.constructor = function Constructor4() {};\n" 1771 "var obj5 = {};\n" 1772 "var obj6 = {};\n" 1773 "obj6.constructor = 6;"); 1774 v8::Local<v8::Object> js_global = 1775 env->Global()->GetPrototype().As<v8::Object>(); 1776 v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>(); 1777 i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1); 1778 CHECK_EQ(0, StringCmp( 1779 "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1))); 1780 v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>(); 1781 i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2); 1782 CHECK_EQ(0, StringCmp( 1783 "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2))); 1784 v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>(); 1785 i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3); 1786 CHECK_EQ(0, StringCmp( 1787 "Constructor3", i::V8HeapExplorer::GetConstructorName(*js_obj3))); 1788 v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>(); 1789 i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4); 1790 CHECK_EQ(0, StringCmp( 1791 "Constructor4", i::V8HeapExplorer::GetConstructorName(*js_obj4))); 1792 v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>(); 1793 i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5); 1794 CHECK_EQ(0, StringCmp( 1795 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5))); 1796 v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>(); 1797 i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6); 1798 CHECK_EQ(0, StringCmp( 1799 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6))); 1800 } 1801 1802 1803 TEST(FastCaseAccessors) { 1804 LocalContext env; 1805 v8::HandleScope scope(env->GetIsolate()); 1806 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1807 1808 CompileRun("var obj1 = {};\n" 1809 "obj1.__defineGetter__('propWithGetter', function Y() {\n" 1810 " return 42;\n" 1811 "});\n" 1812 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n" 1813 " return this.value_ = value;\n" 1814 "});\n"); 1815 const v8::HeapSnapshot* snapshot = 1816 heap_profiler->TakeHeapSnapshot(v8_str("fastCaseAccessors")); 1817 CHECK(ValidateSnapshot(snapshot)); 1818 1819 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1820 CHECK_NE(NULL, global); 1821 const v8::HeapGraphNode* obj1 = 1822 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1"); 1823 CHECK_NE(NULL, obj1); 1824 const v8::HeapGraphNode* func; 1825 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter"); 1826 CHECK_NE(NULL, func); 1827 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter"); 1828 CHECK_EQ(NULL, func); 1829 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter"); 1830 CHECK_NE(NULL, func); 1831 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter"); 1832 CHECK_EQ(NULL, func); 1833 } 1834 1835 1836 TEST(SlowCaseAccessors) { 1837 LocalContext env; 1838 v8::HandleScope scope(env->GetIsolate()); 1839 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1840 1841 CompileRun("var obj1 = {};\n" 1842 "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};" 1843 "obj1.__defineGetter__('propWithGetter', function Y() {\n" 1844 " return 42;\n" 1845 "});\n" 1846 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n" 1847 " return this.value_ = value;\n" 1848 "});\n"); 1849 const v8::HeapSnapshot* snapshot = 1850 heap_profiler->TakeHeapSnapshot(v8_str("slowCaseAccessors")); 1851 CHECK(ValidateSnapshot(snapshot)); 1852 1853 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1854 CHECK_NE(NULL, global); 1855 const v8::HeapGraphNode* obj1 = 1856 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1"); 1857 CHECK_NE(NULL, obj1); 1858 const v8::HeapGraphNode* func; 1859 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter"); 1860 CHECK_NE(NULL, func); 1861 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter"); 1862 CHECK_EQ(NULL, func); 1863 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter"); 1864 CHECK_NE(NULL, func); 1865 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter"); 1866 CHECK_EQ(NULL, func); 1867 } 1868 1869 1870 TEST(HiddenPropertiesFastCase) { 1871 LocalContext env; 1872 v8::HandleScope scope(env->GetIsolate()); 1873 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1874 1875 CompileRun( 1876 "function C(x) { this.a = this; this.b = x; }\n" 1877 "c = new C(2012);\n"); 1878 const v8::HeapSnapshot* snapshot = 1879 heap_profiler->TakeHeapSnapshot(v8_str("HiddenPropertiesFastCase1")); 1880 CHECK(ValidateSnapshot(snapshot)); 1881 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1882 const v8::HeapGraphNode* c = 1883 GetProperty(global, v8::HeapGraphEdge::kProperty, "c"); 1884 CHECK_NE(NULL, c); 1885 const v8::HeapGraphNode* hidden_props = 1886 GetProperty(c, v8::HeapGraphEdge::kInternal, "hidden_properties"); 1887 CHECK_EQ(NULL, hidden_props); 1888 1889 v8::Handle<v8::Value> cHandle = 1890 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "c")); 1891 CHECK(!cHandle.IsEmpty() && cHandle->IsObject()); 1892 cHandle->ToObject()->SetHiddenValue(v8_str("key"), v8_str("val")); 1893 1894 snapshot = heap_profiler->TakeHeapSnapshot( 1895 v8_str("HiddenPropertiesFastCase2")); 1896 CHECK(ValidateSnapshot(snapshot)); 1897 global = GetGlobalObject(snapshot); 1898 c = GetProperty(global, v8::HeapGraphEdge::kProperty, "c"); 1899 CHECK_NE(NULL, c); 1900 hidden_props = GetProperty(c, v8::HeapGraphEdge::kInternal, 1901 "hidden_properties"); 1902 CHECK_NE(NULL, hidden_props); 1903 } 1904 1905 1906 bool HasWeakEdge(const v8::HeapGraphNode* node) { 1907 for (int i = 0; i < node->GetChildrenCount(); ++i) { 1908 const v8::HeapGraphEdge* handle_edge = node->GetChild(i); 1909 if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true; 1910 } 1911 return false; 1912 } 1913 1914 1915 bool HasWeakGlobalHandle() { 1916 v8::Isolate* isolate = CcTest::isolate(); 1917 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 1918 const v8::HeapSnapshot* snapshot = 1919 heap_profiler->TakeHeapSnapshot(v8_str("weaks")); 1920 CHECK(ValidateSnapshot(snapshot)); 1921 const v8::HeapGraphNode* gc_roots = GetNode( 1922 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)"); 1923 CHECK_NE(NULL, gc_roots); 1924 const v8::HeapGraphNode* global_handles = GetNode( 1925 gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)"); 1926 CHECK_NE(NULL, global_handles); 1927 return HasWeakEdge(global_handles); 1928 } 1929 1930 1931 static void PersistentHandleCallback( 1932 const v8::WeakCallbackData<v8::Object, v8::Persistent<v8::Object> >& data) { 1933 data.GetParameter()->Reset(); 1934 delete data.GetParameter(); 1935 } 1936 1937 1938 TEST(WeakGlobalHandle) { 1939 LocalContext env; 1940 v8::HandleScope scope(env->GetIsolate()); 1941 1942 CHECK(!HasWeakGlobalHandle()); 1943 1944 v8::Persistent<v8::Object> handle(env->GetIsolate(), 1945 v8::Object::New(env->GetIsolate())); 1946 handle.SetWeak(&handle, PersistentHandleCallback); 1947 1948 CHECK(HasWeakGlobalHandle()); 1949 } 1950 1951 1952 TEST(SfiAndJsFunctionWeakRefs) { 1953 LocalContext env; 1954 v8::HandleScope scope(env->GetIsolate()); 1955 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1956 1957 CompileRun( 1958 "fun = (function (x) { return function () { return x + 1; } })(1);"); 1959 const v8::HeapSnapshot* snapshot = 1960 heap_profiler->TakeHeapSnapshot(v8_str("fun")); 1961 CHECK(ValidateSnapshot(snapshot)); 1962 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1963 CHECK_NE(NULL, global); 1964 const v8::HeapGraphNode* fun = 1965 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun"); 1966 CHECK(!HasWeakEdge(fun)); 1967 const v8::HeapGraphNode* shared = 1968 GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared"); 1969 CHECK(!HasWeakEdge(shared)); 1970 } 1971 1972 1973 TEST(NoDebugObjectInSnapshot) { 1974 LocalContext env; 1975 v8::HandleScope scope(env->GetIsolate()); 1976 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1977 1978 CHECK(CcTest::i_isolate()->debug()->Load()); 1979 CompileRun("foo = {};"); 1980 const v8::HeapSnapshot* snapshot = 1981 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 1982 CHECK(ValidateSnapshot(snapshot)); 1983 const v8::HeapGraphNode* root = snapshot->GetRoot(); 1984 int globals_count = 0; 1985 for (int i = 0; i < root->GetChildrenCount(); ++i) { 1986 const v8::HeapGraphEdge* edge = root->GetChild(i); 1987 if (edge->GetType() == v8::HeapGraphEdge::kShortcut) { 1988 ++globals_count; 1989 const v8::HeapGraphNode* global = edge->GetToNode(); 1990 const v8::HeapGraphNode* foo = 1991 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo"); 1992 CHECK_NE(NULL, foo); 1993 } 1994 } 1995 CHECK_EQ(1, globals_count); 1996 } 1997 1998 1999 TEST(AllStrongGcRootsHaveNames) { 2000 LocalContext env; 2001 v8::HandleScope scope(env->GetIsolate()); 2002 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2003 2004 CompileRun("foo = {};"); 2005 const v8::HeapSnapshot* snapshot = 2006 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2007 CHECK(ValidateSnapshot(snapshot)); 2008 const v8::HeapGraphNode* gc_roots = GetNode( 2009 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)"); 2010 CHECK_NE(NULL, gc_roots); 2011 const v8::HeapGraphNode* strong_roots = GetNode( 2012 gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)"); 2013 CHECK_NE(NULL, strong_roots); 2014 for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) { 2015 const v8::HeapGraphEdge* edge = strong_roots->GetChild(i); 2016 CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType()); 2017 v8::String::Utf8Value name(edge->GetName()); 2018 CHECK(isalpha(**name)); 2019 } 2020 } 2021 2022 2023 TEST(NoRefsToNonEssentialEntries) { 2024 LocalContext env; 2025 v8::HandleScope scope(env->GetIsolate()); 2026 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2027 CompileRun("global_object = {};\n"); 2028 const v8::HeapSnapshot* snapshot = 2029 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2030 CHECK(ValidateSnapshot(snapshot)); 2031 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2032 const v8::HeapGraphNode* global_object = 2033 GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object"); 2034 CHECK_NE(NULL, global_object); 2035 const v8::HeapGraphNode* properties = 2036 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties"); 2037 CHECK_EQ(NULL, properties); 2038 const v8::HeapGraphNode* elements = 2039 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements"); 2040 CHECK_EQ(NULL, elements); 2041 } 2042 2043 2044 TEST(MapHasDescriptorsAndTransitions) { 2045 LocalContext env; 2046 v8::HandleScope scope(env->GetIsolate()); 2047 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2048 CompileRun("obj = { a: 10 };\n"); 2049 const v8::HeapSnapshot* snapshot = 2050 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2051 CHECK(ValidateSnapshot(snapshot)); 2052 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2053 const v8::HeapGraphNode* global_object = 2054 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj"); 2055 CHECK_NE(NULL, global_object); 2056 2057 const v8::HeapGraphNode* map = 2058 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "map"); 2059 CHECK_NE(NULL, map); 2060 const v8::HeapGraphNode* own_descriptors = GetProperty( 2061 map, v8::HeapGraphEdge::kInternal, "descriptors"); 2062 CHECK_NE(NULL, own_descriptors); 2063 const v8::HeapGraphNode* own_transitions = GetProperty( 2064 map, v8::HeapGraphEdge::kInternal, "transitions"); 2065 CHECK_EQ(NULL, own_transitions); 2066 } 2067 2068 2069 TEST(ManyLocalsInSharedContext) { 2070 LocalContext env; 2071 v8::HandleScope scope(env->GetIsolate()); 2072 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2073 int num_objects = 6000; 2074 CompileRun( 2075 "var n = 6000;" 2076 "var result = [];" 2077 "result.push('(function outer() {');" 2078 "for (var i = 0; i < n; i++) {" 2079 " var f = 'function f_' + i + '() { ';" 2080 " if (i > 0)" 2081 " f += 'f_' + (i - 1) + '();';" 2082 " f += ' }';" 2083 " result.push(f);" 2084 "}" 2085 "result.push('return f_' + (n - 1) + ';');" 2086 "result.push('})()');" 2087 "var ok = eval(result.join('\\n'));"); 2088 const v8::HeapSnapshot* snapshot = 2089 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2090 CHECK(ValidateSnapshot(snapshot)); 2091 2092 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2093 CHECK_NE(NULL, global); 2094 const v8::HeapGraphNode* ok_object = 2095 GetProperty(global, v8::HeapGraphEdge::kProperty, "ok"); 2096 CHECK_NE(NULL, ok_object); 2097 const v8::HeapGraphNode* context_object = 2098 GetProperty(ok_object, v8::HeapGraphEdge::kInternal, "context"); 2099 CHECK_NE(NULL, context_object); 2100 // Check the objects are not duplicated in the context. 2101 CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1, 2102 context_object->GetChildrenCount()); 2103 // Check all the objects have got their names. 2104 // ... well check just every 15th because otherwise it's too slow in debug. 2105 for (int i = 0; i < num_objects - 1; i += 15) { 2106 i::EmbeddedVector<char, 100> var_name; 2107 i::SNPrintF(var_name, "f_%d", i); 2108 const v8::HeapGraphNode* f_object = GetProperty( 2109 context_object, v8::HeapGraphEdge::kContextVariable, var_name.start()); 2110 CHECK_NE(NULL, f_object); 2111 } 2112 } 2113 2114 2115 TEST(AllocationSitesAreVisible) { 2116 LocalContext env; 2117 v8::Isolate* isolate = env->GetIsolate(); 2118 v8::HandleScope scope(isolate); 2119 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 2120 CompileRun( 2121 "fun = function () { var a = [3, 2, 1]; return a; }\n" 2122 "fun();"); 2123 const v8::HeapSnapshot* snapshot = 2124 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2125 CHECK(ValidateSnapshot(snapshot)); 2126 2127 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2128 CHECK_NE(NULL, global); 2129 const v8::HeapGraphNode* fun_code = 2130 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun"); 2131 CHECK_NE(NULL, fun_code); 2132 const v8::HeapGraphNode* literals = 2133 GetProperty(fun_code, v8::HeapGraphEdge::kInternal, "literals"); 2134 CHECK_NE(NULL, literals); 2135 CHECK_EQ(v8::HeapGraphNode::kArray, literals->GetType()); 2136 CHECK_EQ(2, literals->GetChildrenCount()); 2137 2138 // The second value in the literals array should be the boilerplate, 2139 // after an AllocationSite. 2140 const v8::HeapGraphEdge* prop = literals->GetChild(1); 2141 const v8::HeapGraphNode* allocation_site = prop->GetToNode(); 2142 v8::String::Utf8Value name(allocation_site->GetName()); 2143 CHECK_EQ("system / AllocationSite", *name); 2144 const v8::HeapGraphNode* transition_info = 2145 GetProperty(allocation_site, v8::HeapGraphEdge::kInternal, 2146 "transition_info"); 2147 CHECK_NE(NULL, transition_info); 2148 2149 const v8::HeapGraphNode* elements = 2150 GetProperty(transition_info, v8::HeapGraphEdge::kInternal, 2151 "elements"); 2152 CHECK_NE(NULL, elements); 2153 CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType()); 2154 CHECK_EQ(v8::internal::FixedArray::SizeFor(3), 2155 static_cast<int>(elements->GetShallowSize())); 2156 2157 v8::Handle<v8::Value> array_val = 2158 heap_profiler->FindObjectById(transition_info->GetId()); 2159 CHECK(array_val->IsArray()); 2160 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(array_val); 2161 // Verify the array is "a" in the code above. 2162 CHECK_EQ(3, array->Length()); 2163 CHECK_EQ(v8::Integer::New(isolate, 3), 2164 array->Get(v8::Integer::New(isolate, 0))); 2165 CHECK_EQ(v8::Integer::New(isolate, 2), 2166 array->Get(v8::Integer::New(isolate, 1))); 2167 CHECK_EQ(v8::Integer::New(isolate, 1), 2168 array->Get(v8::Integer::New(isolate, 2))); 2169 } 2170 2171 2172 TEST(JSFunctionHasCodeLink) { 2173 LocalContext env; 2174 v8::HandleScope scope(env->GetIsolate()); 2175 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2176 CompileRun("function foo(x, y) { return x + y; }\n"); 2177 const v8::HeapSnapshot* snapshot = 2178 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2179 CHECK(ValidateSnapshot(snapshot)); 2180 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2181 const v8::HeapGraphNode* foo_func = 2182 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo"); 2183 CHECK_NE(NULL, foo_func); 2184 const v8::HeapGraphNode* code = 2185 GetProperty(foo_func, v8::HeapGraphEdge::kInternal, "code"); 2186 CHECK_NE(NULL, code); 2187 } 2188 2189 2190 static const v8::HeapGraphNode* GetNodeByPath(const v8::HeapSnapshot* snapshot, 2191 const char* path[], 2192 int depth) { 2193 const v8::HeapGraphNode* node = snapshot->GetRoot(); 2194 for (int current_depth = 0; current_depth < depth; ++current_depth) { 2195 int i, count = node->GetChildrenCount(); 2196 for (i = 0; i < count; ++i) { 2197 const v8::HeapGraphEdge* edge = node->GetChild(i); 2198 const v8::HeapGraphNode* to_node = edge->GetToNode(); 2199 v8::String::Utf8Value edge_name(edge->GetName()); 2200 v8::String::Utf8Value node_name(to_node->GetName()); 2201 i::EmbeddedVector<char, 100> name; 2202 i::SNPrintF(name, "%s::%s", *edge_name, *node_name); 2203 if (strstr(name.start(), path[current_depth])) { 2204 node = to_node; 2205 break; 2206 } 2207 } 2208 if (i == count) return NULL; 2209 } 2210 return node; 2211 } 2212 2213 2214 TEST(CheckCodeNames) { 2215 LocalContext env; 2216 v8::HandleScope scope(env->GetIsolate()); 2217 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2218 CompileRun("var a = 1.1;"); 2219 const v8::HeapSnapshot* snapshot = 2220 heap_profiler->TakeHeapSnapshot(v8_str("CheckCodeNames")); 2221 CHECK(ValidateSnapshot(snapshot)); 2222 2223 const char* stub_path[] = { 2224 "::(GC roots)", 2225 "::(Strong roots)", 2226 "code_stubs::", 2227 "::(ArraySingleArgumentConstructorStub code)" 2228 }; 2229 const v8::HeapGraphNode* node = GetNodeByPath(snapshot, 2230 stub_path, ARRAY_SIZE(stub_path)); 2231 CHECK_NE(NULL, node); 2232 2233 const char* builtin_path1[] = { 2234 "::(GC roots)", 2235 "::(Builtins)", 2236 "::(KeyedLoadIC_Generic builtin)" 2237 }; 2238 node = GetNodeByPath(snapshot, builtin_path1, ARRAY_SIZE(builtin_path1)); 2239 CHECK_NE(NULL, node); 2240 2241 const char* builtin_path2[] = { 2242 "::(GC roots)", 2243 "::(Builtins)", 2244 "::(CompileUnoptimized builtin)" 2245 }; 2246 node = GetNodeByPath(snapshot, builtin_path2, ARRAY_SIZE(builtin_path2)); 2247 CHECK_NE(NULL, node); 2248 v8::String::Utf8Value node_name(node->GetName()); 2249 CHECK_EQ("(CompileUnoptimized builtin)", *node_name); 2250 } 2251 2252 2253 static const char* record_trace_tree_source = 2254 "var topFunctions = [];\n" 2255 "var global = this;\n" 2256 "function generateFunctions(width, depth) {\n" 2257 " var script = [];\n" 2258 " for (var i = 0; i < width; i++) {\n" 2259 " for (var j = 0; j < depth; j++) {\n" 2260 " script.push('function f_' + i + '_' + j + '(x) {\\n');\n" 2261 " script.push(' try {\\n');\n" 2262 " if (j < depth-2) {\n" 2263 " script.push(' return f_' + i + '_' + (j+1) + '(x+1);\\n');\n" 2264 " } else if (j == depth - 2) {\n" 2265 " script.push(' return new f_' + i + '_' + (depth - 1) + '();\\n');\n" 2266 " } else if (j == depth - 1) {\n" 2267 " script.push(' this.ts = Date.now();\\n');\n" 2268 " }\n" 2269 " script.push(' } catch (e) {}\\n');\n" 2270 " script.push('}\\n');\n" 2271 " \n" 2272 " }\n" 2273 " }\n" 2274 " var script = script.join('');\n" 2275 " // throw script;\n" 2276 " global.eval(script);\n" 2277 " for (var i = 0; i < width; i++) {\n" 2278 " topFunctions.push(this['f_' + i + '_0']);\n" 2279 " }\n" 2280 "}\n" 2281 "\n" 2282 "var width = 3;\n" 2283 "var depth = 3;\n" 2284 "generateFunctions(width, depth);\n" 2285 "var instances = [];\n" 2286 "function start() {\n" 2287 " for (var i = 0; i < width; i++) {\n" 2288 " instances.push(topFunctions[i](0));\n" 2289 " }\n" 2290 "}\n" 2291 "\n" 2292 "for (var i = 0; i < 100; i++) start();\n"; 2293 2294 2295 static AllocationTraceNode* FindNode( 2296 AllocationTracker* tracker, const Vector<const char*>& names) { 2297 AllocationTraceNode* node = tracker->trace_tree()->root(); 2298 for (int i = 0; node != NULL && i < names.length(); i++) { 2299 const char* name = names[i]; 2300 Vector<AllocationTraceNode*> children = node->children(); 2301 node = NULL; 2302 for (int j = 0; j < children.length(); j++) { 2303 unsigned index = children[j]->function_info_index(); 2304 AllocationTracker::FunctionInfo* info = 2305 tracker->function_info_list()[index]; 2306 if (info && strcmp(info->name, name) == 0) { 2307 node = children[j]; 2308 break; 2309 } 2310 } 2311 } 2312 return node; 2313 } 2314 2315 2316 TEST(ArrayGrowLeftTrim) { 2317 LocalContext env; 2318 v8::HandleScope scope(env->GetIsolate()); 2319 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2320 heap_profiler->StartTrackingHeapObjects(true); 2321 2322 CompileRun( 2323 "var a = [];\n" 2324 "for (var i = 0; i < 5; ++i)\n" 2325 " a[i] = i;\n" 2326 "for (var i = 0; i < 3; ++i)\n" 2327 " a.shift();\n"); 2328 2329 const char* names[] = { "(anonymous function)" }; 2330 AllocationTracker* tracker = 2331 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker(); 2332 CHECK_NE(NULL, tracker); 2333 // Resolve all function locations. 2334 tracker->PrepareForSerialization(); 2335 // Print for better diagnostics in case of failure. 2336 tracker->trace_tree()->Print(tracker); 2337 2338 AllocationTraceNode* node = 2339 FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names))); 2340 CHECK_NE(NULL, node); 2341 CHECK_GE(node->allocation_count(), 2); 2342 CHECK_GE(node->allocation_size(), 4 * 5); 2343 heap_profiler->StopTrackingHeapObjects(); 2344 } 2345 2346 2347 TEST(TrackHeapAllocations) { 2348 v8::HandleScope scope(v8::Isolate::GetCurrent()); 2349 LocalContext env; 2350 2351 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2352 heap_profiler->StartTrackingHeapObjects(true); 2353 2354 CompileRun(record_trace_tree_source); 2355 2356 AllocationTracker* tracker = 2357 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker(); 2358 CHECK_NE(NULL, tracker); 2359 // Resolve all function locations. 2360 tracker->PrepareForSerialization(); 2361 // Print for better diagnostics in case of failure. 2362 tracker->trace_tree()->Print(tracker); 2363 2364 const char* names[] = 2365 { "(anonymous function)", "start", "f_0_0", "f_0_1", "f_0_2" }; 2366 AllocationTraceNode* node = 2367 FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names))); 2368 CHECK_NE(NULL, node); 2369 CHECK_GE(node->allocation_count(), 100); 2370 CHECK_GE(node->allocation_size(), 4 * node->allocation_count()); 2371 heap_profiler->StopTrackingHeapObjects(); 2372 } 2373 2374 2375 static const char* inline_heap_allocation_source = 2376 "function f_0(x) {\n" 2377 " return f_1(x+1);\n" 2378 "}\n" 2379 "%NeverOptimizeFunction(f_0);\n" 2380 "function f_1(x) {\n" 2381 " return new f_2(x+1);\n" 2382 "}\n" 2383 "function f_2(x) {\n" 2384 " this.foo = x;\n" 2385 "}\n" 2386 "var instances = [];\n" 2387 "function start() {\n" 2388 " instances.push(f_0(0));\n" 2389 "}\n" 2390 "\n" 2391 "for (var i = 0; i < 100; i++) start();\n"; 2392 2393 2394 TEST(TrackBumpPointerAllocations) { 2395 i::FLAG_allow_natives_syntax = true; 2396 v8::HandleScope scope(v8::Isolate::GetCurrent()); 2397 LocalContext env; 2398 2399 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2400 const char* names[] = { "(anonymous function)", "start", "f_0", "f_1" }; 2401 // First check that normally all allocations are recorded. 2402 { 2403 heap_profiler->StartTrackingHeapObjects(true); 2404 2405 CompileRun(inline_heap_allocation_source); 2406 2407 AllocationTracker* tracker = 2408 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker(); 2409 CHECK_NE(NULL, tracker); 2410 // Resolve all function locations. 2411 tracker->PrepareForSerialization(); 2412 // Print for better diagnostics in case of failure. 2413 tracker->trace_tree()->Print(tracker); 2414 2415 AllocationTraceNode* node = 2416 FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names))); 2417 CHECK_NE(NULL, node); 2418 CHECK_GE(node->allocation_count(), 100); 2419 CHECK_GE(node->allocation_size(), 4 * node->allocation_count()); 2420 heap_profiler->StopTrackingHeapObjects(); 2421 } 2422 2423 { 2424 heap_profiler->StartTrackingHeapObjects(true); 2425 2426 // Now check that not all allocations are tracked if we manually reenable 2427 // inline allocations. 2428 CHECK(CcTest::heap()->inline_allocation_disabled()); 2429 CcTest::heap()->EnableInlineAllocation(); 2430 2431 CompileRun(inline_heap_allocation_source); 2432 2433 AllocationTracker* tracker = 2434 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker(); 2435 CHECK_NE(NULL, tracker); 2436 // Resolve all function locations. 2437 tracker->PrepareForSerialization(); 2438 // Print for better diagnostics in case of failure. 2439 tracker->trace_tree()->Print(tracker); 2440 2441 AllocationTraceNode* node = 2442 FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names))); 2443 CHECK_NE(NULL, node); 2444 CHECK_LT(node->allocation_count(), 100); 2445 2446 CcTest::heap()->DisableInlineAllocation(); 2447 heap_profiler->StopTrackingHeapObjects(); 2448 } 2449 } 2450 2451 2452 TEST(TrackV8ApiAllocation) { 2453 v8::HandleScope scope(v8::Isolate::GetCurrent()); 2454 LocalContext env; 2455 2456 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2457 const char* names[] = { "(V8 API)" }; 2458 heap_profiler->StartTrackingHeapObjects(true); 2459 2460 v8::Handle<v8::Object> o1 = v8::Object::New(env->GetIsolate()); 2461 o1->Clone(); 2462 2463 AllocationTracker* tracker = 2464 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker(); 2465 CHECK_NE(NULL, tracker); 2466 // Resolve all function locations. 2467 tracker->PrepareForSerialization(); 2468 // Print for better diagnostics in case of failure. 2469 tracker->trace_tree()->Print(tracker); 2470 2471 AllocationTraceNode* node = 2472 FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names))); 2473 CHECK_NE(NULL, node); 2474 CHECK_GE(node->allocation_count(), 2); 2475 CHECK_GE(node->allocation_size(), 4 * node->allocation_count()); 2476 heap_profiler->StopTrackingHeapObjects(); 2477 } 2478 2479 2480 TEST(ArrayBufferAndArrayBufferView) { 2481 LocalContext env; 2482 v8::HandleScope scope(env->GetIsolate()); 2483 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2484 CompileRun("arr1 = new Uint32Array(100);\n"); 2485 const v8::HeapSnapshot* snapshot = 2486 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2487 CHECK(ValidateSnapshot(snapshot)); 2488 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2489 const v8::HeapGraphNode* arr1_obj = 2490 GetProperty(global, v8::HeapGraphEdge::kProperty, "arr1"); 2491 CHECK_NE(NULL, arr1_obj); 2492 const v8::HeapGraphNode* arr1_buffer = 2493 GetProperty(arr1_obj, v8::HeapGraphEdge::kInternal, "buffer"); 2494 CHECK_NE(NULL, arr1_buffer); 2495 const v8::HeapGraphNode* first_view = 2496 GetProperty(arr1_buffer, v8::HeapGraphEdge::kWeak, "weak_first_view"); 2497 CHECK_NE(NULL, first_view); 2498 const v8::HeapGraphNode* backing_store = 2499 GetProperty(arr1_buffer, v8::HeapGraphEdge::kInternal, "backing_store"); 2500 CHECK_NE(NULL, backing_store); 2501 CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize())); 2502 } 2503 2504 2505 static int GetRetainersCount(const v8::HeapSnapshot* snapshot, 2506 const v8::HeapGraphNode* node) { 2507 int count = 0; 2508 for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) { 2509 const v8::HeapGraphNode* parent = snapshot->GetNode(i); 2510 for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) { 2511 if (parent->GetChild(j)->GetToNode() == node) { 2512 ++count; 2513 } 2514 } 2515 } 2516 return count; 2517 } 2518 2519 2520 TEST(ArrayBufferSharedBackingStore) { 2521 LocalContext env; 2522 v8::Isolate* isolate = env->GetIsolate(); 2523 v8::HandleScope handle_scope(isolate); 2524 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 2525 2526 v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024); 2527 CHECK_EQ(1024, static_cast<int>(ab->ByteLength())); 2528 CHECK(!ab->IsExternal()); 2529 v8::ArrayBuffer::Contents ab_contents = ab->Externalize(); 2530 CHECK(ab->IsExternal()); 2531 2532 CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength())); 2533 void* data = ab_contents.Data(); 2534 ASSERT(data != NULL); 2535 v8::Local<v8::ArrayBuffer> ab2 = 2536 v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength()); 2537 CHECK(ab2->IsExternal()); 2538 env->Global()->Set(v8_str("ab1"), ab); 2539 env->Global()->Set(v8_str("ab2"), ab2); 2540 2541 v8::Handle<v8::Value> result = CompileRun("ab2.byteLength"); 2542 CHECK_EQ(1024, result->Int32Value()); 2543 2544 const v8::HeapSnapshot* snapshot = 2545 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2546 CHECK(ValidateSnapshot(snapshot)); 2547 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2548 const v8::HeapGraphNode* ab1_node = 2549 GetProperty(global, v8::HeapGraphEdge::kProperty, "ab1"); 2550 CHECK_NE(NULL, ab1_node); 2551 const v8::HeapGraphNode* ab1_data = 2552 GetProperty(ab1_node, v8::HeapGraphEdge::kInternal, "backing_store"); 2553 CHECK_NE(NULL, ab1_data); 2554 const v8::HeapGraphNode* ab2_node = 2555 GetProperty(global, v8::HeapGraphEdge::kProperty, "ab2"); 2556 CHECK_NE(NULL, ab2_node); 2557 const v8::HeapGraphNode* ab2_data = 2558 GetProperty(ab2_node, v8::HeapGraphEdge::kInternal, "backing_store"); 2559 CHECK_NE(NULL, ab2_data); 2560 CHECK_EQ(ab1_data, ab2_data); 2561 CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data)); 2562 free(data); 2563 } 2564 2565 2566 TEST(BoxObject) { 2567 v8::Isolate* isolate = CcTest::isolate(); 2568 v8::HandleScope scope(isolate); 2569 LocalContext env; 2570 v8::Handle<v8::Object> global_proxy = env->Global(); 2571 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 2572 2573 i::Factory* factory = CcTest::i_isolate()->factory(); 2574 i::Handle<i::String> string = factory->NewStringFromStaticAscii("string"); 2575 i::Handle<i::Object> box = factory->NewBox(string); 2576 global->Set(0, v8::ToApiHandle<v8::Object>(box)); 2577 2578 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 2579 const v8::HeapSnapshot* snapshot = 2580 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2581 CHECK(ValidateSnapshot(snapshot)); 2582 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot); 2583 const v8::HeapGraphNode* box_node = 2584 GetProperty(global_node, v8::HeapGraphEdge::kElement, "0"); 2585 CHECK_NE(NULL, box_node); 2586 v8::String::Utf8Value box_node_name(box_node->GetName()); 2587 CHECK_EQ("system / Box", *box_node_name); 2588 const v8::HeapGraphNode* box_value = 2589 GetProperty(box_node, v8::HeapGraphEdge::kInternal, "value"); 2590 CHECK_NE(NULL, box_value); 2591 } 2592 2593 2594 TEST(WeakContainers) { 2595 i::FLAG_allow_natives_syntax = true; 2596 LocalContext env; 2597 v8::HandleScope scope(env->GetIsolate()); 2598 if (!CcTest::i_isolate()->use_crankshaft()) return; 2599 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2600 CompileRun( 2601 "function foo(a) { return a.x; }\n" 2602 "obj = {x : 123};\n" 2603 "foo(obj);\n" 2604 "foo(obj);\n" 2605 "%OptimizeFunctionOnNextCall(foo);\n" 2606 "foo(obj);\n"); 2607 const v8::HeapSnapshot* snapshot = 2608 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2609 CHECK(ValidateSnapshot(snapshot)); 2610 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2611 const v8::HeapGraphNode* obj = 2612 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj"); 2613 CHECK_NE(NULL, obj); 2614 const v8::HeapGraphNode* map = 2615 GetProperty(obj, v8::HeapGraphEdge::kInternal, "map"); 2616 CHECK_NE(NULL, map); 2617 const v8::HeapGraphNode* dependent_code = 2618 GetProperty(map, v8::HeapGraphEdge::kInternal, "dependent_code"); 2619 if (!dependent_code) return; 2620 int count = dependent_code->GetChildrenCount(); 2621 CHECK_NE(0, count); 2622 for (int i = 0; i < count; ++i) { 2623 const v8::HeapGraphEdge* prop = dependent_code->GetChild(i); 2624 CHECK_EQ(v8::HeapGraphEdge::kWeak, prop->GetType()); 2625 } 2626 } 2627 2628 2629 static inline i::Address ToAddress(int n) { 2630 return reinterpret_cast<i::Address>(n); 2631 } 2632 2633 2634 TEST(AddressToTraceMap) { 2635 i::AddressToTraceMap map; 2636 2637 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(150))); 2638 2639 // [0x100, 0x200) -> 1 2640 map.AddRange(ToAddress(0x100), 0x100, 1U); 2641 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x50))); 2642 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x100))); 2643 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x150))); 2644 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x100 + 0x100))); 2645 CHECK_EQ(1, static_cast<int>(map.size())); 2646 2647 // [0x100, 0x200) -> 1, [0x200, 0x300) -> 2 2648 map.AddRange(ToAddress(0x200), 0x100, 2U); 2649 CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x2a0))); 2650 CHECK_EQ(2, static_cast<int>(map.size())); 2651 2652 // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2 2653 map.AddRange(ToAddress(0x180), 0x100, 3U); 2654 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x17F))); 2655 CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x280))); 2656 CHECK_EQ(3, map.GetTraceNodeId(ToAddress(0x180))); 2657 CHECK_EQ(3, static_cast<int>(map.size())); 2658 2659 // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2, 2660 // [0x400, 0x500) -> 4 2661 map.AddRange(ToAddress(0x400), 0x100, 4U); 2662 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x17F))); 2663 CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x280))); 2664 CHECK_EQ(3, map.GetTraceNodeId(ToAddress(0x180))); 2665 CHECK_EQ(4, map.GetTraceNodeId(ToAddress(0x450))); 2666 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x500))); 2667 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x350))); 2668 CHECK_EQ(4, static_cast<int>(map.size())); 2669 2670 // [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5 2671 map.AddRange(ToAddress(0x200), 0x400, 5U); 2672 CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x200))); 2673 CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x400))); 2674 CHECK_EQ(3, static_cast<int>(map.size())); 2675 2676 // [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5 2677 map.AddRange(ToAddress(0x180), 0x80, 6U); 2678 map.AddRange(ToAddress(0x180), 0x80, 7U); 2679 CHECK_EQ(7, map.GetTraceNodeId(ToAddress(0x180))); 2680 CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x200))); 2681 CHECK_EQ(3, static_cast<int>(map.size())); 2682 2683 map.Clear(); 2684 CHECK_EQ(0, static_cast<int>(map.size())); 2685 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x400))); 2686 } 2687