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->NewStringFromStaticChars("0123456789"); 445 i::Handle<i::String> second = factory->NewStringFromStaticChars("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 LocalContext env; 476 v8::HandleScope scope(env->GetIsolate()); 477 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 478 479 CompileRun("a = Symbol('mySymbol');\n"); 480 const v8::HeapSnapshot* snapshot = 481 heap_profiler->TakeHeapSnapshot(v8_str("Symbol")); 482 CHECK(ValidateSnapshot(snapshot)); 483 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 484 const v8::HeapGraphNode* a = 485 GetProperty(global, v8::HeapGraphEdge::kProperty, "a"); 486 CHECK_NE(NULL, a); 487 CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol); 488 CHECK_EQ(v8_str("symbol"), a->GetName()); 489 const v8::HeapGraphNode* name = 490 GetProperty(a, v8::HeapGraphEdge::kInternal, "name"); 491 CHECK_NE(NULL, name); 492 CHECK_EQ(v8_str("mySymbol"), name->GetName()); 493 } 494 495 496 TEST(HeapSnapshotWeakCollection) { 497 LocalContext env; 498 v8::HandleScope scope(env->GetIsolate()); 499 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 500 501 CompileRun( 502 "k = {}; v = {}; s = 'str';\n" 503 "ws = new WeakSet(); ws.add(k); ws.add(v); ws[s] = s;\n" 504 "wm = new WeakMap(); wm.set(k, v); wm[s] = s;\n"); 505 const v8::HeapSnapshot* snapshot = 506 heap_profiler->TakeHeapSnapshot(v8_str("WeakCollections")); 507 CHECK(ValidateSnapshot(snapshot)); 508 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 509 const v8::HeapGraphNode* k = 510 GetProperty(global, v8::HeapGraphEdge::kProperty, "k"); 511 CHECK_NE(NULL, k); 512 const v8::HeapGraphNode* v = 513 GetProperty(global, v8::HeapGraphEdge::kProperty, "v"); 514 CHECK_NE(NULL, v); 515 const v8::HeapGraphNode* s = 516 GetProperty(global, v8::HeapGraphEdge::kProperty, "s"); 517 CHECK_NE(NULL, s); 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 const v8::HeapGraphNode* ws_s = 539 GetProperty(ws, v8::HeapGraphEdge::kProperty, "str"); 540 CHECK_NE(NULL, ws_s); 541 CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(ws_s->GetId())); 542 543 const v8::HeapGraphNode* wm = 544 GetProperty(global, v8::HeapGraphEdge::kProperty, "wm"); 545 CHECK_NE(NULL, wm); 546 CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType()); 547 CHECK_EQ(v8_str("WeakMap"), wm->GetName()); 548 549 const v8::HeapGraphNode* wm_table = 550 GetProperty(wm, v8::HeapGraphEdge::kInternal, "table"); 551 CHECK_EQ(v8::HeapGraphNode::kArray, wm_table->GetType()); 552 CHECK_GT(wm_table->GetChildrenCount(), 0); 553 weak_entries = 0; 554 for (int i = 0, count = wm_table->GetChildrenCount(); i < count; ++i) { 555 const v8::HeapGraphEdge* prop = wm_table->GetChild(i); 556 if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue; 557 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId(); 558 if (to_node_id == k->GetId() || to_node_id == v->GetId()) { 559 ++weak_entries; 560 } 561 } 562 CHECK_EQ(2, weak_entries); 563 const v8::HeapGraphNode* wm_s = 564 GetProperty(wm, v8::HeapGraphEdge::kProperty, "str"); 565 CHECK_NE(NULL, wm_s); 566 CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(wm_s->GetId())); 567 } 568 569 570 TEST(HeapSnapshotCollection) { 571 LocalContext env; 572 v8::HandleScope scope(env->GetIsolate()); 573 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 574 575 CompileRun( 576 "k = {}; v = {}; s = 'str';\n" 577 "set = new Set(); set.add(k); set.add(v); set[s] = s;\n" 578 "map = new Map(); map.set(k, v); map[s] = s;\n"); 579 const v8::HeapSnapshot* snapshot = 580 heap_profiler->TakeHeapSnapshot(v8_str("Collections")); 581 CHECK(ValidateSnapshot(snapshot)); 582 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 583 const v8::HeapGraphNode* k = 584 GetProperty(global, v8::HeapGraphEdge::kProperty, "k"); 585 CHECK_NE(NULL, k); 586 const v8::HeapGraphNode* v = 587 GetProperty(global, v8::HeapGraphEdge::kProperty, "v"); 588 CHECK_NE(NULL, v); 589 const v8::HeapGraphNode* s = 590 GetProperty(global, v8::HeapGraphEdge::kProperty, "s"); 591 CHECK_NE(NULL, s); 592 593 const v8::HeapGraphNode* set = 594 GetProperty(global, v8::HeapGraphEdge::kProperty, "set"); 595 CHECK_NE(NULL, set); 596 CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType()); 597 CHECK_EQ(v8_str("Set"), set->GetName()); 598 599 const v8::HeapGraphNode* set_table = 600 GetProperty(set, v8::HeapGraphEdge::kInternal, "table"); 601 CHECK_EQ(v8::HeapGraphNode::kArray, set_table->GetType()); 602 CHECK_GT(set_table->GetChildrenCount(), 0); 603 int entries = 0; 604 for (int i = 0, count = set_table->GetChildrenCount(); i < count; ++i) { 605 const v8::HeapGraphEdge* prop = set_table->GetChild(i); 606 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId(); 607 if (to_node_id == k->GetId() || to_node_id == v->GetId()) { 608 ++entries; 609 } 610 } 611 CHECK_EQ(2, entries); 612 const v8::HeapGraphNode* set_s = 613 GetProperty(set, v8::HeapGraphEdge::kProperty, "str"); 614 CHECK_NE(NULL, set_s); 615 CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(set_s->GetId())); 616 617 const v8::HeapGraphNode* map = 618 GetProperty(global, v8::HeapGraphEdge::kProperty, "map"); 619 CHECK_NE(NULL, map); 620 CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType()); 621 CHECK_EQ(v8_str("Map"), map->GetName()); 622 623 const v8::HeapGraphNode* map_table = 624 GetProperty(map, v8::HeapGraphEdge::kInternal, "table"); 625 CHECK_EQ(v8::HeapGraphNode::kArray, map_table->GetType()); 626 CHECK_GT(map_table->GetChildrenCount(), 0); 627 entries = 0; 628 for (int i = 0, count = map_table->GetChildrenCount(); i < count; ++i) { 629 const v8::HeapGraphEdge* prop = map_table->GetChild(i); 630 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId(); 631 if (to_node_id == k->GetId() || to_node_id == v->GetId()) { 632 ++entries; 633 } 634 } 635 CHECK_EQ(2, entries); 636 const v8::HeapGraphNode* map_s = 637 GetProperty(map, v8::HeapGraphEdge::kProperty, "str"); 638 CHECK_NE(NULL, map_s); 639 CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(map_s->GetId())); 640 } 641 642 643 TEST(HeapSnapshotInternalReferences) { 644 v8::Isolate* isolate = CcTest::isolate(); 645 v8::HandleScope scope(isolate); 646 v8::Local<v8::ObjectTemplate> global_template = 647 v8::ObjectTemplate::New(isolate); 648 global_template->SetInternalFieldCount(2); 649 LocalContext env(NULL, global_template); 650 v8::Handle<v8::Object> global_proxy = env->Global(); 651 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 652 CHECK_EQ(2, global->InternalFieldCount()); 653 v8::Local<v8::Object> obj = v8::Object::New(isolate); 654 global->SetInternalField(0, v8_num(17)); 655 global->SetInternalField(1, obj); 656 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 657 const v8::HeapSnapshot* snapshot = 658 heap_profiler->TakeHeapSnapshot(v8_str("internals")); 659 CHECK(ValidateSnapshot(snapshot)); 660 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot); 661 // The first reference will not present, because it's a Smi. 662 CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0")); 663 // The second reference is to an object. 664 CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1")); 665 } 666 667 668 // Trying to introduce a check helper for uint32_t causes many 669 // overloading ambiguities, so it seems easier just to cast 670 // them to a signed type. 671 #define CHECK_EQ_SNAPSHOT_OBJECT_ID(a, b) \ 672 CHECK_EQ(static_cast<int32_t>(a), static_cast<int32_t>(b)) 673 #define CHECK_NE_SNAPSHOT_OBJECT_ID(a, b) \ 674 CHECK((a) != (b)) // NOLINT 675 676 TEST(HeapSnapshotAddressReuse) { 677 LocalContext env; 678 v8::HandleScope scope(env->GetIsolate()); 679 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 680 681 CompileRun( 682 "function A() {}\n" 683 "var a = [];\n" 684 "for (var i = 0; i < 10000; ++i)\n" 685 " a[i] = new A();\n"); 686 const v8::HeapSnapshot* snapshot1 = 687 heap_profiler->TakeHeapSnapshot(v8_str("snapshot1")); 688 CHECK(ValidateSnapshot(snapshot1)); 689 v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId(); 690 691 CompileRun( 692 "for (var i = 0; i < 10000; ++i)\n" 693 " a[i] = new A();\n"); 694 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 695 696 const v8::HeapSnapshot* snapshot2 = 697 heap_profiler->TakeHeapSnapshot(v8_str("snapshot2")); 698 CHECK(ValidateSnapshot(snapshot2)); 699 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); 700 701 const v8::HeapGraphNode* array_node = 702 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a"); 703 CHECK_NE(NULL, array_node); 704 int wrong_count = 0; 705 for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) { 706 const v8::HeapGraphEdge* prop = array_node->GetChild(i); 707 if (prop->GetType() != v8::HeapGraphEdge::kElement) 708 continue; 709 v8::SnapshotObjectId id = prop->GetToNode()->GetId(); 710 if (id < maxId1) 711 ++wrong_count; 712 } 713 CHECK_EQ(0, wrong_count); 714 } 715 716 717 TEST(HeapEntryIdsAndArrayShift) { 718 LocalContext env; 719 v8::HandleScope scope(env->GetIsolate()); 720 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 721 722 CompileRun( 723 "function AnObject() {\n" 724 " this.first = 'first';\n" 725 " this.second = 'second';\n" 726 "}\n" 727 "var a = new Array();\n" 728 "for (var i = 0; i < 10; ++i)\n" 729 " a.push(new AnObject());\n"); 730 const v8::HeapSnapshot* snapshot1 = 731 heap_profiler->TakeHeapSnapshot(v8_str("s1")); 732 CHECK(ValidateSnapshot(snapshot1)); 733 734 CompileRun( 735 "for (var i = 0; i < 1; ++i)\n" 736 " a.shift();\n"); 737 738 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 739 740 const v8::HeapSnapshot* snapshot2 = 741 heap_profiler->TakeHeapSnapshot(v8_str("s2")); 742 CHECK(ValidateSnapshot(snapshot2)); 743 744 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1); 745 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); 746 CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId()); 747 CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId()); 748 749 const v8::HeapGraphNode* a1 = 750 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a"); 751 CHECK_NE(NULL, a1); 752 const v8::HeapGraphNode* k1 = 753 GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements"); 754 CHECK_NE(NULL, k1); 755 const v8::HeapGraphNode* a2 = 756 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a"); 757 CHECK_NE(NULL, a2); 758 const v8::HeapGraphNode* k2 = 759 GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements"); 760 CHECK_NE(NULL, k2); 761 762 CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId()); 763 CHECK_EQ_SNAPSHOT_OBJECT_ID(k1->GetId(), k2->GetId()); 764 } 765 766 767 TEST(HeapEntryIdsAndGC) { 768 LocalContext env; 769 v8::HandleScope scope(env->GetIsolate()); 770 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 771 772 CompileRun( 773 "function A() {}\n" 774 "function B(x) { this.x = x; }\n" 775 "var a = new A();\n" 776 "var b = new B(a);"); 777 v8::Local<v8::String> s1_str = v8_str("s1"); 778 v8::Local<v8::String> s2_str = v8_str("s2"); 779 const v8::HeapSnapshot* snapshot1 = 780 heap_profiler->TakeHeapSnapshot(s1_str); 781 CHECK(ValidateSnapshot(snapshot1)); 782 783 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 784 785 const v8::HeapSnapshot* snapshot2 = 786 heap_profiler->TakeHeapSnapshot(s2_str); 787 CHECK(ValidateSnapshot(snapshot2)); 788 789 CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000); 790 CHECK(snapshot1->GetMaxSnapshotJSObjectId() <= 791 snapshot2->GetMaxSnapshotJSObjectId()); 792 793 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1); 794 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2); 795 CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId()); 796 CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId()); 797 const v8::HeapGraphNode* A1 = 798 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A"); 799 CHECK_NE(NULL, A1); 800 const v8::HeapGraphNode* A2 = 801 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A"); 802 CHECK_NE(NULL, A2); 803 CHECK_NE_SNAPSHOT_OBJECT_ID(0, A1->GetId()); 804 CHECK_EQ_SNAPSHOT_OBJECT_ID(A1->GetId(), A2->GetId()); 805 const v8::HeapGraphNode* B1 = 806 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B"); 807 CHECK_NE(NULL, B1); 808 const v8::HeapGraphNode* B2 = 809 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B"); 810 CHECK_NE(NULL, B2); 811 CHECK_NE_SNAPSHOT_OBJECT_ID(0, B1->GetId()); 812 CHECK_EQ_SNAPSHOT_OBJECT_ID(B1->GetId(), B2->GetId()); 813 const v8::HeapGraphNode* a1 = 814 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a"); 815 CHECK_NE(NULL, a1); 816 const v8::HeapGraphNode* a2 = 817 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a"); 818 CHECK_NE(NULL, a2); 819 CHECK_NE_SNAPSHOT_OBJECT_ID(0, a1->GetId()); 820 CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId()); 821 const v8::HeapGraphNode* b1 = 822 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b"); 823 CHECK_NE(NULL, b1); 824 const v8::HeapGraphNode* b2 = 825 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b"); 826 CHECK_NE(NULL, b2); 827 CHECK_NE_SNAPSHOT_OBJECT_ID(0, b1->GetId()); 828 CHECK_EQ_SNAPSHOT_OBJECT_ID(b1->GetId(), b2->GetId()); 829 } 830 831 832 TEST(HeapSnapshotRootPreservedAfterSorting) { 833 LocalContext env; 834 v8::HandleScope scope(env->GetIsolate()); 835 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 836 const v8::HeapSnapshot* snapshot = 837 heap_profiler->TakeHeapSnapshot(v8_str("s")); 838 CHECK(ValidateSnapshot(snapshot)); 839 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); 840 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( 841 snapshot))->GetSortedEntriesList(); 842 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); 843 CHECK_EQ(root1, root2); 844 } 845 846 847 namespace { 848 849 class TestJSONStream : public v8::OutputStream { 850 public: 851 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {} 852 explicit TestJSONStream(int abort_countdown) 853 : eos_signaled_(0), abort_countdown_(abort_countdown) {} 854 virtual ~TestJSONStream() {} 855 virtual void EndOfStream() { ++eos_signaled_; } 856 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) { 857 if (abort_countdown_ > 0) --abort_countdown_; 858 if (abort_countdown_ == 0) return kAbort; 859 CHECK_GT(chars_written, 0); 860 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0'); 861 i::MemCopy(chunk.start(), buffer, chars_written); 862 return kContinue; 863 } 864 virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) { 865 DCHECK(false); 866 return kAbort; 867 } 868 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); } 869 int eos_signaled() { return eos_signaled_; } 870 int size() { return buffer_.size(); } 871 872 private: 873 i::Collector<char> buffer_; 874 int eos_signaled_; 875 int abort_countdown_; 876 }; 877 878 class OneByteResource : public v8::String::ExternalOneByteStringResource { 879 public: 880 explicit OneByteResource(i::Vector<char> string) : data_(string.start()) { 881 length_ = string.length(); 882 } 883 virtual const char* data() const { return data_; } 884 virtual size_t length() const { return length_; } 885 private: 886 const char* data_; 887 size_t length_; 888 }; 889 890 } // namespace 891 892 TEST(HeapSnapshotJSONSerialization) { 893 LocalContext env; 894 v8::HandleScope scope(env->GetIsolate()); 895 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 896 897 #define STRING_LITERAL_FOR_TEST \ 898 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\"" 899 CompileRun( 900 "function A(s) { this.s = s; }\n" 901 "function B(x) { this.x = x; }\n" 902 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n" 903 "var b = new B(a);"); 904 const v8::HeapSnapshot* snapshot = 905 heap_profiler->TakeHeapSnapshot(v8_str("json")); 906 CHECK(ValidateSnapshot(snapshot)); 907 908 TestJSONStream stream; 909 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); 910 CHECK_GT(stream.size(), 0); 911 CHECK_EQ(1, stream.eos_signaled()); 912 i::ScopedVector<char> json(stream.size()); 913 stream.WriteTo(json); 914 915 // Verify that snapshot string is valid JSON. 916 OneByteResource* json_res = new OneByteResource(json); 917 v8::Local<v8::String> json_string = 918 v8::String::NewExternal(env->GetIsolate(), json_res); 919 env->Global()->Set(v8_str("json_snapshot"), json_string); 920 v8::Local<v8::Value> snapshot_parse_result = CompileRun( 921 "var parsed = JSON.parse(json_snapshot); true;"); 922 CHECK(!snapshot_parse_result.IsEmpty()); 923 924 // Verify that snapshot object has required fields. 925 v8::Local<v8::Object> parsed_snapshot = 926 env->Global()->Get(v8_str("parsed"))->ToObject(); 927 CHECK(parsed_snapshot->Has(v8_str("snapshot"))); 928 CHECK(parsed_snapshot->Has(v8_str("nodes"))); 929 CHECK(parsed_snapshot->Has(v8_str("edges"))); 930 CHECK(parsed_snapshot->Has(v8_str("strings"))); 931 932 // Get node and edge "member" offsets. 933 v8::Local<v8::Value> meta_analysis_result = CompileRun( 934 "var meta = parsed.snapshot.meta;\n" 935 "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n" 936 "var node_fields_count = meta.node_fields.length;\n" 937 "var edge_fields_count = meta.edge_fields.length;\n" 938 "var edge_type_offset = meta.edge_fields.indexOf('type');\n" 939 "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n" 940 "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n" 941 "var property_type =" 942 " meta.edge_types[edge_type_offset].indexOf('property');\n" 943 "var shortcut_type =" 944 " meta.edge_types[edge_type_offset].indexOf('shortcut');\n" 945 "var node_count = parsed.nodes.length / node_fields_count;\n" 946 "var first_edge_indexes = parsed.first_edge_indexes = [];\n" 947 "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n" 948 " first_edge_indexes[i] = first_edge_index;\n" 949 " first_edge_index += edge_fields_count *\n" 950 " parsed.nodes[i * node_fields_count + edge_count_offset];\n" 951 "}\n" 952 "first_edge_indexes[node_count] = first_edge_index;\n"); 953 CHECK(!meta_analysis_result.IsEmpty()); 954 955 // A helper function for processing encoded nodes. 956 CompileRun( 957 "function GetChildPosByProperty(pos, prop_name, prop_type) {\n" 958 " var nodes = parsed.nodes;\n" 959 " var edges = parsed.edges;\n" 960 " var strings = parsed.strings;\n" 961 " var node_ordinal = pos / node_fields_count;\n" 962 " for (var i = parsed.first_edge_indexes[node_ordinal],\n" 963 " count = parsed.first_edge_indexes[node_ordinal + 1];\n" 964 " i < count; i += edge_fields_count) {\n" 965 " if (edges[i + edge_type_offset] === prop_type\n" 966 " && strings[edges[i + edge_name_offset]] === prop_name)\n" 967 " return edges[i + edge_to_node_offset];\n" 968 " }\n" 969 " return null;\n" 970 "}\n"); 971 // Get the string index using the path: <root> -> <global>.b.x.s 972 v8::Local<v8::Value> string_obj_pos_val = CompileRun( 973 "GetChildPosByProperty(\n" 974 " GetChildPosByProperty(\n" 975 " GetChildPosByProperty(" 976 " parsed.edges[edge_fields_count + edge_to_node_offset]," 977 " \"b\", property_type),\n" 978 " \"x\", property_type)," 979 " \"s\", property_type)"); 980 CHECK(!string_obj_pos_val.IsEmpty()); 981 int string_obj_pos = 982 static_cast<int>(string_obj_pos_val->ToNumber()->Value()); 983 v8::Local<v8::Object> nodes_array = 984 parsed_snapshot->Get(v8_str("nodes"))->ToObject(); 985 int string_index = static_cast<int>( 986 nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value()); 987 CHECK_GT(string_index, 0); 988 v8::Local<v8::Object> strings_array = 989 parsed_snapshot->Get(v8_str("strings"))->ToObject(); 990 v8::Local<v8::String> string = strings_array->Get(string_index)->ToString(); 991 v8::Local<v8::String> ref_string = 992 CompileRun(STRING_LITERAL_FOR_TEST)->ToString(); 993 #undef STRING_LITERAL_FOR_TEST 994 CHECK_EQ(*v8::String::Utf8Value(ref_string), 995 *v8::String::Utf8Value(string)); 996 } 997 998 999 TEST(HeapSnapshotJSONSerializationAborting) { 1000 LocalContext env; 1001 v8::HandleScope scope(env->GetIsolate()); 1002 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1003 const v8::HeapSnapshot* snapshot = 1004 heap_profiler->TakeHeapSnapshot(v8_str("abort")); 1005 CHECK(ValidateSnapshot(snapshot)); 1006 TestJSONStream stream(5); 1007 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); 1008 CHECK_GT(stream.size(), 0); 1009 CHECK_EQ(0, stream.eos_signaled()); 1010 } 1011 1012 namespace { 1013 1014 class TestStatsStream : public v8::OutputStream { 1015 public: 1016 TestStatsStream() 1017 : eos_signaled_(0), 1018 updates_written_(0), 1019 entries_count_(0), 1020 entries_size_(0), 1021 intervals_count_(0), 1022 first_interval_index_(-1) { } 1023 TestStatsStream(const TestStatsStream& stream) 1024 : v8::OutputStream(stream), 1025 eos_signaled_(stream.eos_signaled_), 1026 updates_written_(stream.updates_written_), 1027 entries_count_(stream.entries_count_), 1028 entries_size_(stream.entries_size_), 1029 intervals_count_(stream.intervals_count_), 1030 first_interval_index_(stream.first_interval_index_) { } 1031 virtual ~TestStatsStream() {} 1032 virtual void EndOfStream() { ++eos_signaled_; } 1033 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) { 1034 DCHECK(false); 1035 return kAbort; 1036 } 1037 virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer, 1038 int updates_written) { 1039 ++intervals_count_; 1040 DCHECK(updates_written); 1041 updates_written_ += updates_written; 1042 entries_count_ = 0; 1043 if (first_interval_index_ == -1 && updates_written != 0) 1044 first_interval_index_ = buffer[0].index; 1045 for (int i = 0; i < updates_written; ++i) { 1046 entries_count_ += buffer[i].count; 1047 entries_size_ += buffer[i].size; 1048 } 1049 1050 return kContinue; 1051 } 1052 int eos_signaled() { return eos_signaled_; } 1053 int updates_written() { return updates_written_; } 1054 uint32_t entries_count() const { return entries_count_; } 1055 uint32_t entries_size() const { return entries_size_; } 1056 int intervals_count() const { return intervals_count_; } 1057 int first_interval_index() const { return first_interval_index_; } 1058 1059 private: 1060 int eos_signaled_; 1061 int updates_written_; 1062 uint32_t entries_count_; 1063 uint32_t entries_size_; 1064 int intervals_count_; 1065 int first_interval_index_; 1066 }; 1067 1068 } // namespace 1069 1070 static TestStatsStream GetHeapStatsUpdate( 1071 v8::HeapProfiler* heap_profiler, 1072 v8::SnapshotObjectId* object_id = NULL) { 1073 TestStatsStream stream; 1074 v8::SnapshotObjectId last_seen_id = heap_profiler->GetHeapStats(&stream); 1075 if (object_id) 1076 *object_id = last_seen_id; 1077 CHECK_EQ(1, stream.eos_signaled()); 1078 return stream; 1079 } 1080 1081 1082 TEST(HeapSnapshotObjectsStats) { 1083 LocalContext env; 1084 v8::HandleScope scope(env->GetIsolate()); 1085 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1086 1087 heap_profiler->StartTrackingHeapObjects(); 1088 // We have to call GC 6 times. In other case the garbage will be 1089 // the reason of flakiness. 1090 for (int i = 0; i < 6; ++i) { 1091 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 1092 } 1093 1094 v8::SnapshotObjectId initial_id; 1095 { 1096 // Single chunk of data expected in update. Initial data. 1097 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler, 1098 &initial_id); 1099 CHECK_EQ(1, stats_update.intervals_count()); 1100 CHECK_EQ(1, stats_update.updates_written()); 1101 CHECK_LT(0, stats_update.entries_size()); 1102 CHECK_EQ(0, stats_update.first_interval_index()); 1103 } 1104 1105 // No data expected in update because nothing has happened. 1106 v8::SnapshotObjectId same_id; 1107 CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written()); 1108 CHECK_EQ_SNAPSHOT_OBJECT_ID(initial_id, same_id); 1109 1110 { 1111 v8::SnapshotObjectId additional_string_id; 1112 v8::HandleScope inner_scope_1(env->GetIsolate()); 1113 v8_str("string1"); 1114 { 1115 // Single chunk of data with one new entry expected in update. 1116 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler, 1117 &additional_string_id); 1118 CHECK_LT(same_id, additional_string_id); 1119 CHECK_EQ(1, stats_update.intervals_count()); 1120 CHECK_EQ(1, stats_update.updates_written()); 1121 CHECK_LT(0, stats_update.entries_size()); 1122 CHECK_EQ(1, stats_update.entries_count()); 1123 CHECK_EQ(2, stats_update.first_interval_index()); 1124 } 1125 1126 // No data expected in update because nothing happened. 1127 v8::SnapshotObjectId last_id; 1128 CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written()); 1129 CHECK_EQ_SNAPSHOT_OBJECT_ID(additional_string_id, last_id); 1130 1131 { 1132 v8::HandleScope inner_scope_2(env->GetIsolate()); 1133 v8_str("string2"); 1134 1135 uint32_t entries_size; 1136 { 1137 v8::HandleScope inner_scope_3(env->GetIsolate()); 1138 v8_str("string3"); 1139 v8_str("string4"); 1140 1141 { 1142 // Single chunk of data with three new entries expected in update. 1143 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1144 CHECK_EQ(1, stats_update.intervals_count()); 1145 CHECK_EQ(1, stats_update.updates_written()); 1146 CHECK_LT(0, entries_size = stats_update.entries_size()); 1147 CHECK_EQ(3, stats_update.entries_count()); 1148 CHECK_EQ(4, stats_update.first_interval_index()); 1149 } 1150 } 1151 1152 { 1153 // Single chunk of data with two left entries expected in update. 1154 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1155 CHECK_EQ(1, stats_update.intervals_count()); 1156 CHECK_EQ(1, stats_update.updates_written()); 1157 CHECK_GT(entries_size, stats_update.entries_size()); 1158 CHECK_EQ(1, stats_update.entries_count()); 1159 // Two strings from forth interval were released. 1160 CHECK_EQ(4, stats_update.first_interval_index()); 1161 } 1162 } 1163 1164 { 1165 // Single chunk of data with 0 left entries expected in update. 1166 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1167 CHECK_EQ(1, stats_update.intervals_count()); 1168 CHECK_EQ(1, stats_update.updates_written()); 1169 CHECK_EQ(0, stats_update.entries_size()); 1170 CHECK_EQ(0, stats_update.entries_count()); 1171 // The last string from forth interval was released. 1172 CHECK_EQ(4, stats_update.first_interval_index()); 1173 } 1174 } 1175 { 1176 // Single chunk of data with 0 left entries expected in update. 1177 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1178 CHECK_EQ(1, stats_update.intervals_count()); 1179 CHECK_EQ(1, stats_update.updates_written()); 1180 CHECK_EQ(0, stats_update.entries_size()); 1181 CHECK_EQ(0, stats_update.entries_count()); 1182 // The only string from the second interval was released. 1183 CHECK_EQ(2, stats_update.first_interval_index()); 1184 } 1185 1186 v8::Local<v8::Array> array = v8::Array::New(env->GetIsolate()); 1187 CHECK_EQ(0, array->Length()); 1188 // Force array's buffer allocation. 1189 array->Set(2, v8_num(7)); 1190 1191 uint32_t entries_size; 1192 { 1193 // Single chunk of data with 2 entries expected in update. 1194 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1195 CHECK_EQ(1, stats_update.intervals_count()); 1196 CHECK_EQ(1, stats_update.updates_written()); 1197 CHECK_LT(0, entries_size = stats_update.entries_size()); 1198 // They are the array and its buffer. 1199 CHECK_EQ(2, stats_update.entries_count()); 1200 CHECK_EQ(8, stats_update.first_interval_index()); 1201 } 1202 1203 for (int i = 0; i < 100; ++i) 1204 array->Set(i, v8_num(i)); 1205 1206 { 1207 // Single chunk of data with 1 entry expected in update. 1208 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler); 1209 CHECK_EQ(1, stats_update.intervals_count()); 1210 // The first interval was changed because old buffer was collected. 1211 // The second interval was changed because new buffer was allocated. 1212 CHECK_EQ(2, stats_update.updates_written()); 1213 CHECK_LT(entries_size, stats_update.entries_size()); 1214 CHECK_EQ(2, stats_update.entries_count()); 1215 CHECK_EQ(8, stats_update.first_interval_index()); 1216 } 1217 1218 heap_profiler->StopTrackingHeapObjects(); 1219 } 1220 1221 1222 TEST(HeapObjectIds) { 1223 LocalContext env; 1224 v8::Isolate* isolate = env->GetIsolate(); 1225 v8::HandleScope scope(isolate); 1226 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1227 1228 const int kLength = 10; 1229 v8::Handle<v8::Object> objects[kLength]; 1230 v8::SnapshotObjectId ids[kLength]; 1231 1232 heap_profiler->StartTrackingHeapObjects(false); 1233 1234 for (int i = 0; i < kLength; i++) { 1235 objects[i] = v8::Object::New(isolate); 1236 } 1237 GetHeapStatsUpdate(heap_profiler); 1238 1239 for (int i = 0; i < kLength; i++) { 1240 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]); 1241 CHECK_NE(v8::HeapProfiler::kUnknownObjectId, static_cast<int>(id)); 1242 ids[i] = id; 1243 } 1244 1245 heap_profiler->StopTrackingHeapObjects(); 1246 CcTest::heap()->CollectAllAvailableGarbage(); 1247 1248 for (int i = 0; i < kLength; i++) { 1249 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]); 1250 CHECK_EQ(static_cast<int>(ids[i]), static_cast<int>(id)); 1251 v8::Handle<v8::Value> obj = heap_profiler->FindObjectById(ids[i]); 1252 CHECK_EQ(objects[i], obj); 1253 } 1254 1255 heap_profiler->ClearObjectIds(); 1256 for (int i = 0; i < kLength; i++) { 1257 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]); 1258 CHECK_EQ(v8::HeapProfiler::kUnknownObjectId, static_cast<int>(id)); 1259 v8::Handle<v8::Value> obj = heap_profiler->FindObjectById(ids[i]); 1260 CHECK(obj.IsEmpty()); 1261 } 1262 } 1263 1264 1265 static void CheckChildrenIds(const v8::HeapSnapshot* snapshot, 1266 const v8::HeapGraphNode* node, 1267 int level, int max_level) { 1268 if (level > max_level) return; 1269 CHECK_EQ(node, snapshot->GetNodeById(node->GetId())); 1270 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { 1271 const v8::HeapGraphEdge* prop = node->GetChild(i); 1272 const v8::HeapGraphNode* child = 1273 snapshot->GetNodeById(prop->GetToNode()->GetId()); 1274 CHECK_EQ_SNAPSHOT_OBJECT_ID(prop->GetToNode()->GetId(), child->GetId()); 1275 CHECK_EQ(prop->GetToNode(), child); 1276 CheckChildrenIds(snapshot, child, level + 1, max_level); 1277 } 1278 } 1279 1280 1281 TEST(HeapSnapshotGetNodeById) { 1282 LocalContext env; 1283 v8::HandleScope scope(env->GetIsolate()); 1284 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1285 1286 const v8::HeapSnapshot* snapshot = 1287 heap_profiler->TakeHeapSnapshot(v8_str("id")); 1288 CHECK(ValidateSnapshot(snapshot)); 1289 const v8::HeapGraphNode* root = snapshot->GetRoot(); 1290 CheckChildrenIds(snapshot, root, 0, 3); 1291 // Check a big id, which should not exist yet. 1292 CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL)); 1293 } 1294 1295 1296 TEST(HeapSnapshotGetSnapshotObjectId) { 1297 LocalContext env; 1298 v8::HandleScope scope(env->GetIsolate()); 1299 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1300 CompileRun("globalObject = {};\n"); 1301 const v8::HeapSnapshot* snapshot = 1302 heap_profiler->TakeHeapSnapshot(v8_str("get_snapshot_object_id")); 1303 CHECK(ValidateSnapshot(snapshot)); 1304 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1305 const v8::HeapGraphNode* global_object = 1306 GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject"); 1307 CHECK(global_object); 1308 1309 v8::Local<v8::Value> globalObjectHandle = env->Global()->Get( 1310 v8::String::NewFromUtf8(env->GetIsolate(), "globalObject")); 1311 CHECK(!globalObjectHandle.IsEmpty()); 1312 CHECK(globalObjectHandle->IsObject()); 1313 1314 v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle); 1315 CHECK_NE(static_cast<int>(v8::HeapProfiler::kUnknownObjectId), 1316 id); 1317 CHECK_EQ(static_cast<int>(id), global_object->GetId()); 1318 } 1319 1320 1321 TEST(HeapSnapshotUnknownSnapshotObjectId) { 1322 LocalContext env; 1323 v8::HandleScope scope(env->GetIsolate()); 1324 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1325 CompileRun("globalObject = {};\n"); 1326 const v8::HeapSnapshot* snapshot = 1327 heap_profiler->TakeHeapSnapshot(v8_str("unknown_object_id")); 1328 CHECK(ValidateSnapshot(snapshot)); 1329 const v8::HeapGraphNode* node = 1330 snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId); 1331 CHECK_EQ(NULL, node); 1332 } 1333 1334 1335 namespace { 1336 1337 class TestActivityControl : public v8::ActivityControl { 1338 public: 1339 explicit TestActivityControl(int abort_count) 1340 : done_(0), total_(0), abort_count_(abort_count) {} 1341 ControlOption ReportProgressValue(int done, int total) { 1342 done_ = done; 1343 total_ = total; 1344 return --abort_count_ != 0 ? kContinue : kAbort; 1345 } 1346 int done() { return done_; } 1347 int total() { return total_; } 1348 1349 private: 1350 int done_; 1351 int total_; 1352 int abort_count_; 1353 }; 1354 } 1355 1356 1357 TEST(TakeHeapSnapshotAborting) { 1358 LocalContext env; 1359 v8::HandleScope scope(env->GetIsolate()); 1360 1361 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1362 const int snapshots_count = heap_profiler->GetSnapshotCount(); 1363 TestActivityControl aborting_control(1); 1364 const v8::HeapSnapshot* no_snapshot = 1365 heap_profiler->TakeHeapSnapshot(v8_str("abort"), 1366 &aborting_control); 1367 CHECK_EQ(NULL, no_snapshot); 1368 CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount()); 1369 CHECK_GT(aborting_control.total(), aborting_control.done()); 1370 1371 TestActivityControl control(-1); // Don't abort. 1372 const v8::HeapSnapshot* snapshot = 1373 heap_profiler->TakeHeapSnapshot(v8_str("full"), 1374 &control); 1375 CHECK(ValidateSnapshot(snapshot)); 1376 1377 CHECK_NE(NULL, snapshot); 1378 CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount()); 1379 CHECK_EQ(control.total(), control.done()); 1380 CHECK_GT(control.total(), 0); 1381 } 1382 1383 1384 namespace { 1385 1386 class TestRetainedObjectInfo : public v8::RetainedObjectInfo { 1387 public: 1388 TestRetainedObjectInfo(int hash, 1389 const char* group_label, 1390 const char* label, 1391 intptr_t element_count = -1, 1392 intptr_t size = -1) 1393 : disposed_(false), 1394 hash_(hash), 1395 group_label_(group_label), 1396 label_(label), 1397 element_count_(element_count), 1398 size_(size) { 1399 instances.Add(this); 1400 } 1401 virtual ~TestRetainedObjectInfo() {} 1402 virtual void Dispose() { 1403 CHECK(!disposed_); 1404 disposed_ = true; 1405 } 1406 virtual bool IsEquivalent(RetainedObjectInfo* other) { 1407 return GetHash() == other->GetHash(); 1408 } 1409 virtual intptr_t GetHash() { return hash_; } 1410 virtual const char* GetGroupLabel() { return group_label_; } 1411 virtual const char* GetLabel() { return label_; } 1412 virtual intptr_t GetElementCount() { return element_count_; } 1413 virtual intptr_t GetSizeInBytes() { return size_; } 1414 bool disposed() { return disposed_; } 1415 1416 static v8::RetainedObjectInfo* WrapperInfoCallback( 1417 uint16_t class_id, v8::Handle<v8::Value> wrapper) { 1418 if (class_id == 1) { 1419 if (wrapper->IsString()) { 1420 v8::String::Utf8Value utf8(wrapper); 1421 if (strcmp(*utf8, "AAA") == 0) 1422 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100); 1423 else if (strcmp(*utf8, "BBB") == 0) 1424 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100); 1425 } 1426 } else if (class_id == 2) { 1427 if (wrapper->IsString()) { 1428 v8::String::Utf8Value utf8(wrapper); 1429 if (strcmp(*utf8, "CCC") == 0) 1430 return new TestRetainedObjectInfo(2, "ccc-group", "ccc"); 1431 } 1432 } 1433 CHECK(false); 1434 return NULL; 1435 } 1436 1437 static i::List<TestRetainedObjectInfo*> instances; 1438 1439 private: 1440 bool disposed_; 1441 int hash_; 1442 const char* group_label_; 1443 const char* label_; 1444 intptr_t element_count_; 1445 intptr_t size_; 1446 }; 1447 1448 1449 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances; 1450 } 1451 1452 1453 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent, 1454 v8::HeapGraphNode::Type type, 1455 const char* name) { 1456 for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) { 1457 const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode(); 1458 if (node->GetType() == type && strcmp(name, 1459 const_cast<i::HeapEntry*>( 1460 reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) { 1461 return node; 1462 } 1463 } 1464 return NULL; 1465 } 1466 1467 1468 TEST(HeapSnapshotRetainedObjectInfo) { 1469 LocalContext env; 1470 v8::Isolate* isolate = env->GetIsolate(); 1471 v8::HandleScope scope(isolate); 1472 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 1473 1474 heap_profiler->SetWrapperClassInfoProvider( 1475 1, TestRetainedObjectInfo::WrapperInfoCallback); 1476 heap_profiler->SetWrapperClassInfoProvider( 1477 2, TestRetainedObjectInfo::WrapperInfoCallback); 1478 v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA")); 1479 p_AAA.SetWrapperClassId(1); 1480 v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB")); 1481 p_BBB.SetWrapperClassId(1); 1482 v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC")); 1483 p_CCC.SetWrapperClassId(2); 1484 CHECK_EQ(0, TestRetainedObjectInfo::instances.length()); 1485 const v8::HeapSnapshot* snapshot = 1486 heap_profiler->TakeHeapSnapshot(v8_str("retained")); 1487 CHECK(ValidateSnapshot(snapshot)); 1488 1489 CHECK_EQ(3, TestRetainedObjectInfo::instances.length()); 1490 for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) { 1491 CHECK(TestRetainedObjectInfo::instances[i]->disposed()); 1492 delete TestRetainedObjectInfo::instances[i]; 1493 } 1494 1495 const v8::HeapGraphNode* native_group_aaa = GetNode( 1496 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group"); 1497 CHECK_NE(NULL, native_group_aaa); 1498 CHECK_EQ(1, native_group_aaa->GetChildrenCount()); 1499 const v8::HeapGraphNode* aaa = GetNode( 1500 native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries"); 1501 CHECK_NE(NULL, aaa); 1502 CHECK_EQ(2, aaa->GetChildrenCount()); 1503 1504 const v8::HeapGraphNode* native_group_ccc = GetNode( 1505 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group"); 1506 const v8::HeapGraphNode* ccc = GetNode( 1507 native_group_ccc, v8::HeapGraphNode::kNative, "ccc"); 1508 CHECK_NE(NULL, ccc); 1509 1510 const v8::HeapGraphNode* n_AAA = GetNode( 1511 aaa, v8::HeapGraphNode::kString, "AAA"); 1512 CHECK_NE(NULL, n_AAA); 1513 const v8::HeapGraphNode* n_BBB = GetNode( 1514 aaa, v8::HeapGraphNode::kString, "BBB"); 1515 CHECK_NE(NULL, n_BBB); 1516 CHECK_EQ(1, ccc->GetChildrenCount()); 1517 const v8::HeapGraphNode* n_CCC = GetNode( 1518 ccc, v8::HeapGraphNode::kString, "CCC"); 1519 CHECK_NE(NULL, n_CCC); 1520 1521 CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native")); 1522 CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native")); 1523 CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native")); 1524 } 1525 1526 1527 class GraphWithImplicitRefs { 1528 public: 1529 static const int kObjectsCount = 4; 1530 explicit GraphWithImplicitRefs(LocalContext* env) { 1531 CHECK_EQ(NULL, instance_); 1532 instance_ = this; 1533 isolate_ = (*env)->GetIsolate(); 1534 for (int i = 0; i < kObjectsCount; i++) { 1535 objects_[i].Reset(isolate_, v8::Object::New(isolate_)); 1536 } 1537 (*env)->Global()->Set(v8_str("root_object"), 1538 v8::Local<v8::Value>::New(isolate_, objects_[0])); 1539 } 1540 ~GraphWithImplicitRefs() { 1541 instance_ = NULL; 1542 } 1543 1544 static void gcPrologue(v8::GCType type, v8::GCCallbackFlags flags) { 1545 instance_->AddImplicitReferences(); 1546 } 1547 1548 private: 1549 void AddImplicitReferences() { 1550 // 0 -> 1 1551 isolate_->SetObjectGroupId(objects_[0], 1552 v8::UniqueId(1)); 1553 isolate_->SetReferenceFromGroup( 1554 v8::UniqueId(1), objects_[1]); 1555 // Adding two more references: 1 -> 2, 1 -> 3 1556 isolate_->SetReference(objects_[1].As<v8::Object>(), 1557 objects_[2]); 1558 isolate_->SetReference(objects_[1].As<v8::Object>(), 1559 objects_[3]); 1560 } 1561 1562 v8::Persistent<v8::Value> objects_[kObjectsCount]; 1563 static GraphWithImplicitRefs* instance_; 1564 v8::Isolate* isolate_; 1565 }; 1566 1567 GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL; 1568 1569 1570 TEST(HeapSnapshotImplicitReferences) { 1571 LocalContext env; 1572 v8::HandleScope scope(env->GetIsolate()); 1573 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1574 1575 GraphWithImplicitRefs graph(&env); 1576 v8::V8::AddGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue); 1577 1578 const v8::HeapSnapshot* snapshot = 1579 heap_profiler->TakeHeapSnapshot(v8_str("implicit_refs")); 1580 CHECK(ValidateSnapshot(snapshot)); 1581 1582 const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot); 1583 const v8::HeapGraphNode* obj0 = GetProperty( 1584 global_object, v8::HeapGraphEdge::kProperty, "root_object"); 1585 CHECK(obj0); 1586 CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType()); 1587 const v8::HeapGraphNode* obj1 = GetProperty( 1588 obj0, v8::HeapGraphEdge::kInternal, "native"); 1589 CHECK(obj1); 1590 int implicit_targets_count = 0; 1591 for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) { 1592 const v8::HeapGraphEdge* prop = obj1->GetChild(i); 1593 v8::String::Utf8Value prop_name(prop->GetName()); 1594 if (prop->GetType() == v8::HeapGraphEdge::kInternal && 1595 strcmp("native", *prop_name) == 0) { 1596 ++implicit_targets_count; 1597 } 1598 } 1599 CHECK_EQ(2, implicit_targets_count); 1600 v8::V8::RemoveGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue); 1601 } 1602 1603 1604 TEST(DeleteAllHeapSnapshots) { 1605 LocalContext env; 1606 v8::HandleScope scope(env->GetIsolate()); 1607 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1608 1609 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1610 heap_profiler->DeleteAllHeapSnapshots(); 1611 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1612 CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("1"))); 1613 CHECK_EQ(1, heap_profiler->GetSnapshotCount()); 1614 heap_profiler->DeleteAllHeapSnapshots(); 1615 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1616 CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("1"))); 1617 CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("2"))); 1618 CHECK_EQ(2, heap_profiler->GetSnapshotCount()); 1619 heap_profiler->DeleteAllHeapSnapshots(); 1620 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1621 } 1622 1623 1624 static const v8::HeapSnapshot* FindHeapSnapshot(v8::HeapProfiler* profiler, 1625 unsigned uid) { 1626 int length = profiler->GetSnapshotCount(); 1627 for (int i = 0; i < length; i++) { 1628 const v8::HeapSnapshot* snapshot = profiler->GetHeapSnapshot(i); 1629 if (snapshot->GetUid() == uid) { 1630 return snapshot; 1631 } 1632 } 1633 return NULL; 1634 } 1635 1636 1637 TEST(DeleteHeapSnapshot) { 1638 LocalContext env; 1639 v8::HandleScope scope(env->GetIsolate()); 1640 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1641 1642 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1643 const v8::HeapSnapshot* s1 = 1644 heap_profiler->TakeHeapSnapshot(v8_str("1")); 1645 1646 CHECK_NE(NULL, s1); 1647 CHECK_EQ(1, heap_profiler->GetSnapshotCount()); 1648 unsigned uid1 = s1->GetUid(); 1649 CHECK_EQ(s1, FindHeapSnapshot(heap_profiler, uid1)); 1650 const_cast<v8::HeapSnapshot*>(s1)->Delete(); 1651 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1652 CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid1)); 1653 1654 const v8::HeapSnapshot* s2 = 1655 heap_profiler->TakeHeapSnapshot(v8_str("2")); 1656 CHECK_NE(NULL, s2); 1657 CHECK_EQ(1, heap_profiler->GetSnapshotCount()); 1658 unsigned uid2 = s2->GetUid(); 1659 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2)); 1660 CHECK_EQ(s2, FindHeapSnapshot(heap_profiler, uid2)); 1661 const v8::HeapSnapshot* s3 = 1662 heap_profiler->TakeHeapSnapshot(v8_str("3")); 1663 CHECK_NE(NULL, s3); 1664 CHECK_EQ(2, heap_profiler->GetSnapshotCount()); 1665 unsigned uid3 = s3->GetUid(); 1666 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3)); 1667 CHECK_EQ(s3, FindHeapSnapshot(heap_profiler, uid3)); 1668 const_cast<v8::HeapSnapshot*>(s2)->Delete(); 1669 CHECK_EQ(1, heap_profiler->GetSnapshotCount()); 1670 CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid2)); 1671 CHECK_EQ(s3, FindHeapSnapshot(heap_profiler, uid3)); 1672 const_cast<v8::HeapSnapshot*>(s3)->Delete(); 1673 CHECK_EQ(0, heap_profiler->GetSnapshotCount()); 1674 CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid3)); 1675 } 1676 1677 1678 class NameResolver : public v8::HeapProfiler::ObjectNameResolver { 1679 public: 1680 virtual const char* GetName(v8::Handle<v8::Object> object) { 1681 return "Global object name"; 1682 } 1683 }; 1684 1685 1686 TEST(GlobalObjectName) { 1687 LocalContext env; 1688 v8::HandleScope scope(env->GetIsolate()); 1689 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1690 1691 CompileRun("document = { URL:\"abcdefgh\" };"); 1692 1693 NameResolver name_resolver; 1694 const v8::HeapSnapshot* snapshot = 1695 heap_profiler->TakeHeapSnapshot(v8_str("document"), 1696 NULL, 1697 &name_resolver); 1698 CHECK(ValidateSnapshot(snapshot)); 1699 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1700 CHECK_NE(NULL, global); 1701 CHECK_EQ("Object / Global object name" , 1702 const_cast<i::HeapEntry*>( 1703 reinterpret_cast<const i::HeapEntry*>(global))->name()); 1704 } 1705 1706 1707 TEST(GlobalObjectFields) { 1708 LocalContext env; 1709 v8::HandleScope scope(env->GetIsolate()); 1710 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1711 CompileRun("obj = {};"); 1712 const v8::HeapSnapshot* snapshot = 1713 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 1714 CHECK(ValidateSnapshot(snapshot)); 1715 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1716 const v8::HeapGraphNode* builtins = 1717 GetProperty(global, v8::HeapGraphEdge::kInternal, "builtins"); 1718 CHECK_NE(NULL, builtins); 1719 const v8::HeapGraphNode* native_context = 1720 GetProperty(global, v8::HeapGraphEdge::kInternal, "native_context"); 1721 CHECK_NE(NULL, native_context); 1722 const v8::HeapGraphNode* global_context = 1723 GetProperty(global, v8::HeapGraphEdge::kInternal, "global_context"); 1724 CHECK_NE(NULL, global_context); 1725 const v8::HeapGraphNode* global_proxy = 1726 GetProperty(global, v8::HeapGraphEdge::kInternal, "global_proxy"); 1727 CHECK_NE(NULL, global_proxy); 1728 } 1729 1730 1731 TEST(NoHandleLeaks) { 1732 LocalContext env; 1733 v8::HandleScope scope(env->GetIsolate()); 1734 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1735 1736 CompileRun("document = { URL:\"abcdefgh\" };"); 1737 1738 v8::Handle<v8::String> name(v8_str("leakz")); 1739 i::Isolate* isolate = CcTest::i_isolate(); 1740 int count_before = i::HandleScope::NumberOfHandles(isolate); 1741 heap_profiler->TakeHeapSnapshot(name); 1742 int count_after = i::HandleScope::NumberOfHandles(isolate); 1743 CHECK_EQ(count_before, count_after); 1744 } 1745 1746 1747 TEST(NodesIteration) { 1748 LocalContext env; 1749 v8::HandleScope scope(env->GetIsolate()); 1750 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1751 const v8::HeapSnapshot* snapshot = 1752 heap_profiler->TakeHeapSnapshot(v8_str("iteration")); 1753 CHECK(ValidateSnapshot(snapshot)); 1754 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1755 CHECK_NE(NULL, global); 1756 // Verify that we can find this object by iteration. 1757 const int nodes_count = snapshot->GetNodesCount(); 1758 int count = 0; 1759 for (int i = 0; i < nodes_count; ++i) { 1760 if (snapshot->GetNode(i) == global) 1761 ++count; 1762 } 1763 CHECK_EQ(1, count); 1764 } 1765 1766 1767 TEST(GetHeapValueForNode) { 1768 LocalContext env; 1769 v8::HandleScope scope(env->GetIsolate()); 1770 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1771 1772 CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };"); 1773 const v8::HeapSnapshot* snapshot = 1774 heap_profiler->TakeHeapSnapshot(v8_str("value")); 1775 CHECK(ValidateSnapshot(snapshot)); 1776 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1777 CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject()); 1778 v8::Local<v8::Object> js_global = 1779 env->Global()->GetPrototype().As<v8::Object>(); 1780 CHECK(js_global == heap_profiler->FindObjectById(global->GetId())); 1781 const v8::HeapGraphNode* obj = GetProperty( 1782 global, v8::HeapGraphEdge::kProperty, "a"); 1783 CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject()); 1784 v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>(); 1785 CHECK(js_obj == heap_profiler->FindObjectById(obj->GetId())); 1786 const v8::HeapGraphNode* s_prop = 1787 GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop"); 1788 v8::Local<v8::String> js_s_prop = 1789 js_obj->Get(v8_str("s_prop")).As<v8::String>(); 1790 CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId())); 1791 const v8::HeapGraphNode* n_prop = 1792 GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop"); 1793 v8::Local<v8::String> js_n_prop = 1794 js_obj->Get(v8_str("n_prop")).As<v8::String>(); 1795 CHECK(js_n_prop == heap_profiler->FindObjectById(n_prop->GetId())); 1796 } 1797 1798 1799 TEST(GetHeapValueForDeletedObject) { 1800 LocalContext env; 1801 v8::HandleScope scope(env->GetIsolate()); 1802 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1803 1804 // It is impossible to delete a global property, so we are about to delete a 1805 // property of the "a" object. Also, the "p" object can't be an empty one 1806 // because the empty object is static and isn't actually deleted. 1807 CompileRun("a = { p: { r: {} } };"); 1808 const v8::HeapSnapshot* snapshot = 1809 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 1810 CHECK(ValidateSnapshot(snapshot)); 1811 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1812 const v8::HeapGraphNode* obj = GetProperty( 1813 global, v8::HeapGraphEdge::kProperty, "a"); 1814 const v8::HeapGraphNode* prop = GetProperty( 1815 obj, v8::HeapGraphEdge::kProperty, "p"); 1816 { 1817 // Perform the check inside a nested local scope to avoid creating a 1818 // reference to the object we are deleting. 1819 v8::HandleScope scope(env->GetIsolate()); 1820 CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject()); 1821 } 1822 CompileRun("delete a.p;"); 1823 CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty()); 1824 } 1825 1826 1827 static int StringCmp(const char* ref, i::String* act) { 1828 i::SmartArrayPointer<char> s_act = act->ToCString(); 1829 int result = strcmp(ref, s_act.get()); 1830 if (result != 0) 1831 fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get()); 1832 return result; 1833 } 1834 1835 1836 TEST(GetConstructorName) { 1837 LocalContext env; 1838 v8::HandleScope scope(env->GetIsolate()); 1839 1840 CompileRun( 1841 "function Constructor1() {};\n" 1842 "var obj1 = new Constructor1();\n" 1843 "var Constructor2 = function() {};\n" 1844 "var obj2 = new Constructor2();\n" 1845 "var obj3 = {};\n" 1846 "obj3.constructor = function Constructor3() {};\n" 1847 "var obj4 = {};\n" 1848 "// Slow properties\n" 1849 "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n" 1850 "obj4.constructor = function Constructor4() {};\n" 1851 "var obj5 = {};\n" 1852 "var obj6 = {};\n" 1853 "obj6.constructor = 6;"); 1854 v8::Local<v8::Object> js_global = 1855 env->Global()->GetPrototype().As<v8::Object>(); 1856 v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>(); 1857 i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1); 1858 CHECK_EQ(0, StringCmp( 1859 "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1))); 1860 v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>(); 1861 i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2); 1862 CHECK_EQ(0, StringCmp( 1863 "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2))); 1864 v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>(); 1865 i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3); 1866 // TODO(verwaest): Restore to Constructor3 once supported by the 1867 // heap-snapshot-generator. 1868 CHECK_EQ( 1869 0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj3))); 1870 v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>(); 1871 i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4); 1872 // TODO(verwaest): Restore to Constructor4 once supported by the 1873 // heap-snapshot-generator. 1874 CHECK_EQ( 1875 0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj4))); 1876 v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>(); 1877 i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5); 1878 CHECK_EQ(0, StringCmp( 1879 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5))); 1880 v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>(); 1881 i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6); 1882 CHECK_EQ(0, StringCmp( 1883 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6))); 1884 } 1885 1886 1887 TEST(FastCaseAccessors) { 1888 LocalContext env; 1889 v8::HandleScope scope(env->GetIsolate()); 1890 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1891 1892 CompileRun("var obj1 = {};\n" 1893 "obj1.__defineGetter__('propWithGetter', function Y() {\n" 1894 " return 42;\n" 1895 "});\n" 1896 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n" 1897 " return this.value_ = value;\n" 1898 "});\n"); 1899 const v8::HeapSnapshot* snapshot = 1900 heap_profiler->TakeHeapSnapshot(v8_str("fastCaseAccessors")); 1901 CHECK(ValidateSnapshot(snapshot)); 1902 1903 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1904 CHECK_NE(NULL, global); 1905 const v8::HeapGraphNode* obj1 = 1906 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1"); 1907 CHECK_NE(NULL, obj1); 1908 const v8::HeapGraphNode* func; 1909 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter"); 1910 CHECK_NE(NULL, func); 1911 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter"); 1912 CHECK_EQ(NULL, func); 1913 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter"); 1914 CHECK_NE(NULL, func); 1915 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter"); 1916 CHECK_EQ(NULL, func); 1917 } 1918 1919 1920 TEST(SlowCaseAccessors) { 1921 LocalContext env; 1922 v8::HandleScope scope(env->GetIsolate()); 1923 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1924 1925 CompileRun("var obj1 = {};\n" 1926 "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};" 1927 "obj1.__defineGetter__('propWithGetter', function Y() {\n" 1928 " return 42;\n" 1929 "});\n" 1930 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n" 1931 " return this.value_ = value;\n" 1932 "});\n"); 1933 const v8::HeapSnapshot* snapshot = 1934 heap_profiler->TakeHeapSnapshot(v8_str("slowCaseAccessors")); 1935 CHECK(ValidateSnapshot(snapshot)); 1936 1937 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1938 CHECK_NE(NULL, global); 1939 const v8::HeapGraphNode* obj1 = 1940 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1"); 1941 CHECK_NE(NULL, obj1); 1942 const v8::HeapGraphNode* func; 1943 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter"); 1944 CHECK_NE(NULL, func); 1945 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter"); 1946 CHECK_EQ(NULL, func); 1947 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter"); 1948 CHECK_NE(NULL, func); 1949 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter"); 1950 CHECK_EQ(NULL, func); 1951 } 1952 1953 1954 TEST(HiddenPropertiesFastCase) { 1955 LocalContext env; 1956 v8::HandleScope scope(env->GetIsolate()); 1957 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1958 1959 CompileRun( 1960 "function C(x) { this.a = this; this.b = x; }\n" 1961 "c = new C(2012);\n"); 1962 const v8::HeapSnapshot* snapshot = 1963 heap_profiler->TakeHeapSnapshot(v8_str("HiddenPropertiesFastCase1")); 1964 CHECK(ValidateSnapshot(snapshot)); 1965 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 1966 const v8::HeapGraphNode* c = 1967 GetProperty(global, v8::HeapGraphEdge::kProperty, "c"); 1968 CHECK_NE(NULL, c); 1969 const v8::HeapGraphNode* hidden_props = 1970 GetProperty(c, v8::HeapGraphEdge::kInternal, "hidden_properties"); 1971 CHECK_EQ(NULL, hidden_props); 1972 1973 v8::Handle<v8::Value> cHandle = 1974 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "c")); 1975 CHECK(!cHandle.IsEmpty() && cHandle->IsObject()); 1976 cHandle->ToObject()->SetHiddenValue(v8_str("key"), v8_str("val")); 1977 1978 snapshot = heap_profiler->TakeHeapSnapshot( 1979 v8_str("HiddenPropertiesFastCase2")); 1980 CHECK(ValidateSnapshot(snapshot)); 1981 global = GetGlobalObject(snapshot); 1982 c = GetProperty(global, v8::HeapGraphEdge::kProperty, "c"); 1983 CHECK_NE(NULL, c); 1984 hidden_props = GetProperty(c, v8::HeapGraphEdge::kInternal, 1985 "hidden_properties"); 1986 CHECK_NE(NULL, hidden_props); 1987 } 1988 1989 1990 TEST(AccessorInfo) { 1991 LocalContext env; 1992 v8::HandleScope scope(env->GetIsolate()); 1993 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 1994 1995 CompileRun("function foo(x) { }\n"); 1996 const v8::HeapSnapshot* snapshot = 1997 heap_profiler->TakeHeapSnapshot(v8_str("AccessorInfoTest")); 1998 CHECK(ValidateSnapshot(snapshot)); 1999 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2000 const v8::HeapGraphNode* foo = 2001 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo"); 2002 CHECK_NE(NULL, foo); 2003 const v8::HeapGraphNode* map = 2004 GetProperty(foo, v8::HeapGraphEdge::kInternal, "map"); 2005 CHECK_NE(NULL, map); 2006 const v8::HeapGraphNode* descriptors = 2007 GetProperty(map, v8::HeapGraphEdge::kInternal, "descriptors"); 2008 CHECK_NE(NULL, descriptors); 2009 const v8::HeapGraphNode* length_name = 2010 GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "2"); 2011 CHECK_NE(NULL, length_name); 2012 CHECK_EQ("length", *v8::String::Utf8Value(length_name->GetName())); 2013 const v8::HeapGraphNode* length_accessor = 2014 GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "4"); 2015 CHECK_NE(NULL, length_accessor); 2016 CHECK_EQ("system / ExecutableAccessorInfo", 2017 *v8::String::Utf8Value(length_accessor->GetName())); 2018 const v8::HeapGraphNode* name = 2019 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "name"); 2020 CHECK_NE(NULL, name); 2021 const v8::HeapGraphNode* getter = 2022 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "getter"); 2023 CHECK_NE(NULL, getter); 2024 const v8::HeapGraphNode* setter = 2025 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "setter"); 2026 CHECK_NE(NULL, setter); 2027 } 2028 2029 2030 bool HasWeakEdge(const v8::HeapGraphNode* node) { 2031 for (int i = 0; i < node->GetChildrenCount(); ++i) { 2032 const v8::HeapGraphEdge* handle_edge = node->GetChild(i); 2033 if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true; 2034 } 2035 return false; 2036 } 2037 2038 2039 bool HasWeakGlobalHandle() { 2040 v8::Isolate* isolate = CcTest::isolate(); 2041 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 2042 const v8::HeapSnapshot* snapshot = 2043 heap_profiler->TakeHeapSnapshot(v8_str("weaks")); 2044 CHECK(ValidateSnapshot(snapshot)); 2045 const v8::HeapGraphNode* gc_roots = GetNode( 2046 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)"); 2047 CHECK_NE(NULL, gc_roots); 2048 const v8::HeapGraphNode* global_handles = GetNode( 2049 gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)"); 2050 CHECK_NE(NULL, global_handles); 2051 return HasWeakEdge(global_handles); 2052 } 2053 2054 2055 static void PersistentHandleCallback( 2056 const v8::WeakCallbackData<v8::Object, v8::Persistent<v8::Object> >& data) { 2057 data.GetParameter()->Reset(); 2058 delete data.GetParameter(); 2059 } 2060 2061 2062 TEST(WeakGlobalHandle) { 2063 LocalContext env; 2064 v8::HandleScope scope(env->GetIsolate()); 2065 2066 CHECK(!HasWeakGlobalHandle()); 2067 2068 v8::Persistent<v8::Object> handle(env->GetIsolate(), 2069 v8::Object::New(env->GetIsolate())); 2070 handle.SetWeak(&handle, PersistentHandleCallback); 2071 2072 CHECK(HasWeakGlobalHandle()); 2073 } 2074 2075 2076 TEST(SfiAndJsFunctionWeakRefs) { 2077 LocalContext env; 2078 v8::HandleScope scope(env->GetIsolate()); 2079 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2080 2081 CompileRun( 2082 "fun = (function (x) { return function () { return x + 1; } })(1);"); 2083 const v8::HeapSnapshot* snapshot = 2084 heap_profiler->TakeHeapSnapshot(v8_str("fun")); 2085 CHECK(ValidateSnapshot(snapshot)); 2086 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2087 CHECK_NE(NULL, global); 2088 const v8::HeapGraphNode* fun = 2089 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun"); 2090 CHECK(!HasWeakEdge(fun)); 2091 const v8::HeapGraphNode* shared = 2092 GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared"); 2093 CHECK(!HasWeakEdge(shared)); 2094 } 2095 2096 2097 TEST(NoDebugObjectInSnapshot) { 2098 LocalContext env; 2099 v8::HandleScope scope(env->GetIsolate()); 2100 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2101 2102 CHECK(CcTest::i_isolate()->debug()->Load()); 2103 CompileRun("foo = {};"); 2104 const v8::HeapSnapshot* snapshot = 2105 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2106 CHECK(ValidateSnapshot(snapshot)); 2107 const v8::HeapGraphNode* root = snapshot->GetRoot(); 2108 int globals_count = 0; 2109 for (int i = 0; i < root->GetChildrenCount(); ++i) { 2110 const v8::HeapGraphEdge* edge = root->GetChild(i); 2111 if (edge->GetType() == v8::HeapGraphEdge::kShortcut) { 2112 ++globals_count; 2113 const v8::HeapGraphNode* global = edge->GetToNode(); 2114 const v8::HeapGraphNode* foo = 2115 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo"); 2116 CHECK_NE(NULL, foo); 2117 } 2118 } 2119 CHECK_EQ(1, globals_count); 2120 } 2121 2122 2123 TEST(AllStrongGcRootsHaveNames) { 2124 LocalContext env; 2125 v8::HandleScope scope(env->GetIsolate()); 2126 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2127 2128 CompileRun("foo = {};"); 2129 const v8::HeapSnapshot* snapshot = 2130 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2131 CHECK(ValidateSnapshot(snapshot)); 2132 const v8::HeapGraphNode* gc_roots = GetNode( 2133 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)"); 2134 CHECK_NE(NULL, gc_roots); 2135 const v8::HeapGraphNode* strong_roots = GetNode( 2136 gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)"); 2137 CHECK_NE(NULL, strong_roots); 2138 for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) { 2139 const v8::HeapGraphEdge* edge = strong_roots->GetChild(i); 2140 CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType()); 2141 v8::String::Utf8Value name(edge->GetName()); 2142 CHECK(isalpha(**name)); 2143 } 2144 } 2145 2146 2147 TEST(NoRefsToNonEssentialEntries) { 2148 LocalContext env; 2149 v8::HandleScope scope(env->GetIsolate()); 2150 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2151 CompileRun("global_object = {};\n"); 2152 const v8::HeapSnapshot* snapshot = 2153 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2154 CHECK(ValidateSnapshot(snapshot)); 2155 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2156 const v8::HeapGraphNode* global_object = 2157 GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object"); 2158 CHECK_NE(NULL, global_object); 2159 const v8::HeapGraphNode* properties = 2160 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties"); 2161 CHECK_EQ(NULL, properties); 2162 const v8::HeapGraphNode* elements = 2163 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements"); 2164 CHECK_EQ(NULL, elements); 2165 } 2166 2167 2168 TEST(MapHasDescriptorsAndTransitions) { 2169 LocalContext env; 2170 v8::HandleScope scope(env->GetIsolate()); 2171 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2172 CompileRun("obj = { a: 10 };\n"); 2173 const v8::HeapSnapshot* snapshot = 2174 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2175 CHECK(ValidateSnapshot(snapshot)); 2176 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2177 const v8::HeapGraphNode* global_object = 2178 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj"); 2179 CHECK_NE(NULL, global_object); 2180 2181 const v8::HeapGraphNode* map = 2182 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "map"); 2183 CHECK_NE(NULL, map); 2184 const v8::HeapGraphNode* own_descriptors = GetProperty( 2185 map, v8::HeapGraphEdge::kInternal, "descriptors"); 2186 CHECK_NE(NULL, own_descriptors); 2187 const v8::HeapGraphNode* own_transitions = GetProperty( 2188 map, v8::HeapGraphEdge::kInternal, "transitions"); 2189 CHECK_EQ(NULL, own_transitions); 2190 } 2191 2192 2193 TEST(ManyLocalsInSharedContext) { 2194 LocalContext env; 2195 v8::HandleScope scope(env->GetIsolate()); 2196 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2197 int num_objects = 6000; 2198 CompileRun( 2199 "var n = 6000;" 2200 "var result = [];" 2201 "result.push('(function outer() {');" 2202 "for (var i = 0; i < n; i++) {" 2203 " var f = 'function f_' + i + '() { ';" 2204 " if (i > 0)" 2205 " f += 'f_' + (i - 1) + '();';" 2206 " f += ' }';" 2207 " result.push(f);" 2208 "}" 2209 "result.push('return f_' + (n - 1) + ';');" 2210 "result.push('})()');" 2211 "var ok = eval(result.join('\\n'));"); 2212 const v8::HeapSnapshot* snapshot = 2213 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2214 CHECK(ValidateSnapshot(snapshot)); 2215 2216 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2217 CHECK_NE(NULL, global); 2218 const v8::HeapGraphNode* ok_object = 2219 GetProperty(global, v8::HeapGraphEdge::kProperty, "ok"); 2220 CHECK_NE(NULL, ok_object); 2221 const v8::HeapGraphNode* context_object = 2222 GetProperty(ok_object, v8::HeapGraphEdge::kInternal, "context"); 2223 CHECK_NE(NULL, context_object); 2224 // Check the objects are not duplicated in the context. 2225 CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1, 2226 context_object->GetChildrenCount()); 2227 // Check all the objects have got their names. 2228 // ... well check just every 15th because otherwise it's too slow in debug. 2229 for (int i = 0; i < num_objects - 1; i += 15) { 2230 i::EmbeddedVector<char, 100> var_name; 2231 i::SNPrintF(var_name, "f_%d", i); 2232 const v8::HeapGraphNode* f_object = GetProperty( 2233 context_object, v8::HeapGraphEdge::kContextVariable, var_name.start()); 2234 CHECK_NE(NULL, f_object); 2235 } 2236 } 2237 2238 2239 TEST(AllocationSitesAreVisible) { 2240 LocalContext env; 2241 v8::Isolate* isolate = env->GetIsolate(); 2242 v8::HandleScope scope(isolate); 2243 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 2244 CompileRun( 2245 "fun = function () { var a = [3, 2, 1]; return a; }\n" 2246 "fun();"); 2247 const v8::HeapSnapshot* snapshot = 2248 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2249 CHECK(ValidateSnapshot(snapshot)); 2250 2251 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2252 CHECK_NE(NULL, global); 2253 const v8::HeapGraphNode* fun_code = 2254 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun"); 2255 CHECK_NE(NULL, fun_code); 2256 const v8::HeapGraphNode* literals = 2257 GetProperty(fun_code, v8::HeapGraphEdge::kInternal, "literals"); 2258 CHECK_NE(NULL, literals); 2259 CHECK_EQ(v8::HeapGraphNode::kArray, literals->GetType()); 2260 CHECK_EQ(2, literals->GetChildrenCount()); 2261 2262 // The second value in the literals array should be the boilerplate, 2263 // after an AllocationSite. 2264 const v8::HeapGraphEdge* prop = literals->GetChild(1); 2265 const v8::HeapGraphNode* allocation_site = prop->GetToNode(); 2266 v8::String::Utf8Value name(allocation_site->GetName()); 2267 CHECK_EQ("system / AllocationSite", *name); 2268 const v8::HeapGraphNode* transition_info = 2269 GetProperty(allocation_site, v8::HeapGraphEdge::kInternal, 2270 "transition_info"); 2271 CHECK_NE(NULL, transition_info); 2272 2273 const v8::HeapGraphNode* elements = 2274 GetProperty(transition_info, v8::HeapGraphEdge::kInternal, 2275 "elements"); 2276 CHECK_NE(NULL, elements); 2277 CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType()); 2278 CHECK_EQ(v8::internal::FixedArray::SizeFor(3), 2279 static_cast<int>(elements->GetShallowSize())); 2280 2281 v8::Handle<v8::Value> array_val = 2282 heap_profiler->FindObjectById(transition_info->GetId()); 2283 CHECK(array_val->IsArray()); 2284 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(array_val); 2285 // Verify the array is "a" in the code above. 2286 CHECK_EQ(3, array->Length()); 2287 CHECK_EQ(v8::Integer::New(isolate, 3), 2288 array->Get(v8::Integer::New(isolate, 0))); 2289 CHECK_EQ(v8::Integer::New(isolate, 2), 2290 array->Get(v8::Integer::New(isolate, 1))); 2291 CHECK_EQ(v8::Integer::New(isolate, 1), 2292 array->Get(v8::Integer::New(isolate, 2))); 2293 } 2294 2295 2296 TEST(JSFunctionHasCodeLink) { 2297 LocalContext env; 2298 v8::HandleScope scope(env->GetIsolate()); 2299 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2300 CompileRun("function foo(x, y) { return x + y; }\n"); 2301 const v8::HeapSnapshot* snapshot = 2302 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2303 CHECK(ValidateSnapshot(snapshot)); 2304 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2305 const v8::HeapGraphNode* foo_func = 2306 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo"); 2307 CHECK_NE(NULL, foo_func); 2308 const v8::HeapGraphNode* code = 2309 GetProperty(foo_func, v8::HeapGraphEdge::kInternal, "code"); 2310 CHECK_NE(NULL, code); 2311 } 2312 2313 2314 static const v8::HeapGraphNode* GetNodeByPath(const v8::HeapSnapshot* snapshot, 2315 const char* path[], 2316 int depth) { 2317 const v8::HeapGraphNode* node = snapshot->GetRoot(); 2318 for (int current_depth = 0; current_depth < depth; ++current_depth) { 2319 int i, count = node->GetChildrenCount(); 2320 for (i = 0; i < count; ++i) { 2321 const v8::HeapGraphEdge* edge = node->GetChild(i); 2322 const v8::HeapGraphNode* to_node = edge->GetToNode(); 2323 v8::String::Utf8Value edge_name(edge->GetName()); 2324 v8::String::Utf8Value node_name(to_node->GetName()); 2325 i::EmbeddedVector<char, 100> name; 2326 i::SNPrintF(name, "%s::%s", *edge_name, *node_name); 2327 if (strstr(name.start(), path[current_depth])) { 2328 node = to_node; 2329 break; 2330 } 2331 } 2332 if (i == count) return NULL; 2333 } 2334 return node; 2335 } 2336 2337 2338 TEST(CheckCodeNames) { 2339 LocalContext env; 2340 v8::HandleScope scope(env->GetIsolate()); 2341 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2342 CompileRun("var a = 1.1;"); 2343 const v8::HeapSnapshot* snapshot = 2344 heap_profiler->TakeHeapSnapshot(v8_str("CheckCodeNames")); 2345 CHECK(ValidateSnapshot(snapshot)); 2346 2347 const char* stub_path[] = { 2348 "::(GC roots)", 2349 "::(Strong roots)", 2350 "code_stubs::", 2351 "::(ArraySingleArgumentConstructorStub code)" 2352 }; 2353 const v8::HeapGraphNode* node = GetNodeByPath(snapshot, 2354 stub_path, arraysize(stub_path)); 2355 CHECK_NE(NULL, node); 2356 2357 const char* builtin_path1[] = { 2358 "::(GC roots)", 2359 "::(Builtins)", 2360 "::(KeyedLoadIC_Generic builtin)" 2361 }; 2362 node = GetNodeByPath(snapshot, builtin_path1, arraysize(builtin_path1)); 2363 CHECK_NE(NULL, node); 2364 2365 const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)", 2366 "::(CompileLazy builtin)"}; 2367 node = GetNodeByPath(snapshot, builtin_path2, arraysize(builtin_path2)); 2368 CHECK_NE(NULL, node); 2369 v8::String::Utf8Value node_name(node->GetName()); 2370 CHECK_EQ("(CompileLazy builtin)", *node_name); 2371 } 2372 2373 2374 static const char* record_trace_tree_source = 2375 "var topFunctions = [];\n" 2376 "var global = this;\n" 2377 "function generateFunctions(width, depth) {\n" 2378 " var script = [];\n" 2379 " for (var i = 0; i < width; i++) {\n" 2380 " for (var j = 0; j < depth; j++) {\n" 2381 " script.push('function f_' + i + '_' + j + '(x) {\\n');\n" 2382 " script.push(' try {\\n');\n" 2383 " if (j < depth-2) {\n" 2384 " script.push(' return f_' + i + '_' + (j+1) + '(x+1);\\n');\n" 2385 " } else if (j == depth - 2) {\n" 2386 " script.push(' return new f_' + i + '_' + (depth - 1) + '();\\n');\n" 2387 " } else if (j == depth - 1) {\n" 2388 " script.push(' this.ts = Date.now();\\n');\n" 2389 " }\n" 2390 " script.push(' } catch (e) {}\\n');\n" 2391 " script.push('}\\n');\n" 2392 " \n" 2393 " }\n" 2394 " }\n" 2395 " var script = script.join('');\n" 2396 " // throw script;\n" 2397 " global.eval(script);\n" 2398 " for (var i = 0; i < width; i++) {\n" 2399 " topFunctions.push(this['f_' + i + '_0']);\n" 2400 " }\n" 2401 "}\n" 2402 "\n" 2403 "var width = 3;\n" 2404 "var depth = 3;\n" 2405 "generateFunctions(width, depth);\n" 2406 "var instances = [];\n" 2407 "function start() {\n" 2408 " for (var i = 0; i < width; i++) {\n" 2409 " instances.push(topFunctions[i](0));\n" 2410 " }\n" 2411 "}\n" 2412 "\n" 2413 "for (var i = 0; i < 100; i++) start();\n"; 2414 2415 2416 static AllocationTraceNode* FindNode( 2417 AllocationTracker* tracker, const Vector<const char*>& names) { 2418 AllocationTraceNode* node = tracker->trace_tree()->root(); 2419 for (int i = 0; node != NULL && i < names.length(); i++) { 2420 const char* name = names[i]; 2421 Vector<AllocationTraceNode*> children = node->children(); 2422 node = NULL; 2423 for (int j = 0; j < children.length(); j++) { 2424 unsigned index = children[j]->function_info_index(); 2425 AllocationTracker::FunctionInfo* info = 2426 tracker->function_info_list()[index]; 2427 if (info && strcmp(info->name, name) == 0) { 2428 node = children[j]; 2429 break; 2430 } 2431 } 2432 } 2433 return node; 2434 } 2435 2436 2437 TEST(ArrayGrowLeftTrim) { 2438 LocalContext env; 2439 v8::HandleScope scope(env->GetIsolate()); 2440 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2441 heap_profiler->StartTrackingHeapObjects(true); 2442 2443 CompileRun( 2444 "var a = [];\n" 2445 "for (var i = 0; i < 5; ++i)\n" 2446 " a[i] = i;\n" 2447 "for (var i = 0; i < 3; ++i)\n" 2448 " a.shift();\n"); 2449 2450 const char* names[] = {""}; 2451 AllocationTracker* tracker = 2452 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker(); 2453 CHECK_NE(NULL, tracker); 2454 // Resolve all function locations. 2455 tracker->PrepareForSerialization(); 2456 // Print for better diagnostics in case of failure. 2457 tracker->trace_tree()->Print(tracker); 2458 2459 AllocationTraceNode* node = 2460 FindNode(tracker, Vector<const char*>(names, arraysize(names))); 2461 CHECK_NE(NULL, node); 2462 CHECK_GE(node->allocation_count(), 2); 2463 CHECK_GE(node->allocation_size(), 4 * 5); 2464 heap_profiler->StopTrackingHeapObjects(); 2465 } 2466 2467 2468 TEST(TrackHeapAllocations) { 2469 v8::HandleScope scope(v8::Isolate::GetCurrent()); 2470 LocalContext env; 2471 2472 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2473 heap_profiler->StartTrackingHeapObjects(true); 2474 2475 CompileRun(record_trace_tree_source); 2476 2477 AllocationTracker* tracker = 2478 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker(); 2479 CHECK_NE(NULL, tracker); 2480 // Resolve all function locations. 2481 tracker->PrepareForSerialization(); 2482 // Print for better diagnostics in case of failure. 2483 tracker->trace_tree()->Print(tracker); 2484 2485 const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"}; 2486 AllocationTraceNode* node = 2487 FindNode(tracker, Vector<const char*>(names, arraysize(names))); 2488 CHECK_NE(NULL, node); 2489 CHECK_GE(node->allocation_count(), 100); 2490 CHECK_GE(node->allocation_size(), 4 * node->allocation_count()); 2491 heap_profiler->StopTrackingHeapObjects(); 2492 } 2493 2494 2495 static const char* inline_heap_allocation_source = 2496 "function f_0(x) {\n" 2497 " return f_1(x+1);\n" 2498 "}\n" 2499 "%NeverOptimizeFunction(f_0);\n" 2500 "function f_1(x) {\n" 2501 " return new f_2(x+1);\n" 2502 "}\n" 2503 "function f_2(x) {\n" 2504 " this.foo = x;\n" 2505 "}\n" 2506 "var instances = [];\n" 2507 "function start() {\n" 2508 " instances.push(f_0(0));\n" 2509 "}\n" 2510 "\n" 2511 "for (var i = 0; i < 100; i++) start();\n"; 2512 2513 2514 TEST(TrackBumpPointerAllocations) { 2515 i::FLAG_allow_natives_syntax = true; 2516 v8::HandleScope scope(v8::Isolate::GetCurrent()); 2517 LocalContext env; 2518 2519 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2520 const char* names[] = {"", "start", "f_0", "f_1"}; 2521 // First check that normally all allocations are recorded. 2522 { 2523 heap_profiler->StartTrackingHeapObjects(true); 2524 2525 CompileRun(inline_heap_allocation_source); 2526 2527 AllocationTracker* tracker = 2528 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker(); 2529 CHECK_NE(NULL, tracker); 2530 // Resolve all function locations. 2531 tracker->PrepareForSerialization(); 2532 // Print for better diagnostics in case of failure. 2533 tracker->trace_tree()->Print(tracker); 2534 2535 AllocationTraceNode* node = 2536 FindNode(tracker, Vector<const char*>(names, arraysize(names))); 2537 CHECK_NE(NULL, node); 2538 CHECK_GE(node->allocation_count(), 100); 2539 CHECK_GE(node->allocation_size(), 4 * node->allocation_count()); 2540 heap_profiler->StopTrackingHeapObjects(); 2541 } 2542 2543 { 2544 heap_profiler->StartTrackingHeapObjects(true); 2545 2546 // Now check that not all allocations are tracked if we manually reenable 2547 // inline allocations. 2548 CHECK(CcTest::heap()->inline_allocation_disabled()); 2549 CcTest::heap()->EnableInlineAllocation(); 2550 2551 CompileRun(inline_heap_allocation_source); 2552 2553 AllocationTracker* tracker = 2554 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker(); 2555 CHECK_NE(NULL, tracker); 2556 // Resolve all function locations. 2557 tracker->PrepareForSerialization(); 2558 // Print for better diagnostics in case of failure. 2559 tracker->trace_tree()->Print(tracker); 2560 2561 AllocationTraceNode* node = 2562 FindNode(tracker, Vector<const char*>(names, arraysize(names))); 2563 CHECK_NE(NULL, node); 2564 CHECK_LT(node->allocation_count(), 100); 2565 2566 CcTest::heap()->DisableInlineAllocation(); 2567 heap_profiler->StopTrackingHeapObjects(); 2568 } 2569 } 2570 2571 2572 TEST(TrackV8ApiAllocation) { 2573 v8::HandleScope scope(v8::Isolate::GetCurrent()); 2574 LocalContext env; 2575 2576 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2577 const char* names[] = { "(V8 API)" }; 2578 heap_profiler->StartTrackingHeapObjects(true); 2579 2580 v8::Handle<v8::Object> o1 = v8::Object::New(env->GetIsolate()); 2581 o1->Clone(); 2582 2583 AllocationTracker* tracker = 2584 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker(); 2585 CHECK_NE(NULL, tracker); 2586 // Resolve all function locations. 2587 tracker->PrepareForSerialization(); 2588 // Print for better diagnostics in case of failure. 2589 tracker->trace_tree()->Print(tracker); 2590 2591 AllocationTraceNode* node = 2592 FindNode(tracker, Vector<const char*>(names, arraysize(names))); 2593 CHECK_NE(NULL, node); 2594 CHECK_GE(node->allocation_count(), 2); 2595 CHECK_GE(node->allocation_size(), 4 * node->allocation_count()); 2596 heap_profiler->StopTrackingHeapObjects(); 2597 } 2598 2599 2600 TEST(ArrayBufferAndArrayBufferView) { 2601 LocalContext env; 2602 v8::HandleScope scope(env->GetIsolate()); 2603 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2604 CompileRun("arr1 = new Uint32Array(100);\n"); 2605 const v8::HeapSnapshot* snapshot = 2606 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2607 CHECK(ValidateSnapshot(snapshot)); 2608 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2609 const v8::HeapGraphNode* arr1_obj = 2610 GetProperty(global, v8::HeapGraphEdge::kProperty, "arr1"); 2611 CHECK_NE(NULL, arr1_obj); 2612 const v8::HeapGraphNode* arr1_buffer = 2613 GetProperty(arr1_obj, v8::HeapGraphEdge::kInternal, "buffer"); 2614 CHECK_NE(NULL, arr1_buffer); 2615 const v8::HeapGraphNode* first_view = 2616 GetProperty(arr1_buffer, v8::HeapGraphEdge::kWeak, "weak_first_view"); 2617 CHECK_NE(NULL, first_view); 2618 const v8::HeapGraphNode* backing_store = 2619 GetProperty(arr1_buffer, v8::HeapGraphEdge::kInternal, "backing_store"); 2620 CHECK_NE(NULL, backing_store); 2621 CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize())); 2622 } 2623 2624 2625 static int GetRetainersCount(const v8::HeapSnapshot* snapshot, 2626 const v8::HeapGraphNode* node) { 2627 int count = 0; 2628 for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) { 2629 const v8::HeapGraphNode* parent = snapshot->GetNode(i); 2630 for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) { 2631 if (parent->GetChild(j)->GetToNode() == node) { 2632 ++count; 2633 } 2634 } 2635 } 2636 return count; 2637 } 2638 2639 2640 TEST(ArrayBufferSharedBackingStore) { 2641 LocalContext env; 2642 v8::Isolate* isolate = env->GetIsolate(); 2643 v8::HandleScope handle_scope(isolate); 2644 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 2645 2646 v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024); 2647 CHECK_EQ(1024, static_cast<int>(ab->ByteLength())); 2648 CHECK(!ab->IsExternal()); 2649 v8::ArrayBuffer::Contents ab_contents = ab->Externalize(); 2650 CHECK(ab->IsExternal()); 2651 2652 CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength())); 2653 void* data = ab_contents.Data(); 2654 DCHECK(data != NULL); 2655 v8::Local<v8::ArrayBuffer> ab2 = 2656 v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength()); 2657 CHECK(ab2->IsExternal()); 2658 env->Global()->Set(v8_str("ab1"), ab); 2659 env->Global()->Set(v8_str("ab2"), ab2); 2660 2661 v8::Handle<v8::Value> result = CompileRun("ab2.byteLength"); 2662 CHECK_EQ(1024, result->Int32Value()); 2663 2664 const v8::HeapSnapshot* snapshot = 2665 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2666 CHECK(ValidateSnapshot(snapshot)); 2667 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2668 const v8::HeapGraphNode* ab1_node = 2669 GetProperty(global, v8::HeapGraphEdge::kProperty, "ab1"); 2670 CHECK_NE(NULL, ab1_node); 2671 const v8::HeapGraphNode* ab1_data = 2672 GetProperty(ab1_node, v8::HeapGraphEdge::kInternal, "backing_store"); 2673 CHECK_NE(NULL, ab1_data); 2674 const v8::HeapGraphNode* ab2_node = 2675 GetProperty(global, v8::HeapGraphEdge::kProperty, "ab2"); 2676 CHECK_NE(NULL, ab2_node); 2677 const v8::HeapGraphNode* ab2_data = 2678 GetProperty(ab2_node, v8::HeapGraphEdge::kInternal, "backing_store"); 2679 CHECK_NE(NULL, ab2_data); 2680 CHECK_EQ(ab1_data, ab2_data); 2681 CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data)); 2682 free(data); 2683 } 2684 2685 2686 TEST(BoxObject) { 2687 v8::Isolate* isolate = CcTest::isolate(); 2688 v8::HandleScope scope(isolate); 2689 LocalContext env; 2690 v8::Handle<v8::Object> global_proxy = env->Global(); 2691 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 2692 2693 i::Factory* factory = CcTest::i_isolate()->factory(); 2694 i::Handle<i::String> string = factory->NewStringFromStaticChars("string"); 2695 i::Handle<i::Object> box = factory->NewBox(string); 2696 global->Set(0, v8::ToApiHandle<v8::Object>(box)); 2697 2698 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); 2699 const v8::HeapSnapshot* snapshot = 2700 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2701 CHECK(ValidateSnapshot(snapshot)); 2702 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot); 2703 const v8::HeapGraphNode* box_node = 2704 GetProperty(global_node, v8::HeapGraphEdge::kElement, "0"); 2705 CHECK_NE(NULL, box_node); 2706 v8::String::Utf8Value box_node_name(box_node->GetName()); 2707 CHECK_EQ("system / Box", *box_node_name); 2708 const v8::HeapGraphNode* box_value = 2709 GetProperty(box_node, v8::HeapGraphEdge::kInternal, "value"); 2710 CHECK_NE(NULL, box_value); 2711 } 2712 2713 2714 TEST(WeakContainers) { 2715 i::FLAG_allow_natives_syntax = true; 2716 LocalContext env; 2717 v8::HandleScope scope(env->GetIsolate()); 2718 if (!CcTest::i_isolate()->use_crankshaft()) return; 2719 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); 2720 CompileRun( 2721 "function foo(a) { return a.x; }\n" 2722 "obj = {x : 123};\n" 2723 "foo(obj);\n" 2724 "foo(obj);\n" 2725 "%OptimizeFunctionOnNextCall(foo);\n" 2726 "foo(obj);\n"); 2727 const v8::HeapSnapshot* snapshot = 2728 heap_profiler->TakeHeapSnapshot(v8_str("snapshot")); 2729 CHECK(ValidateSnapshot(snapshot)); 2730 const v8::HeapGraphNode* global = GetGlobalObject(snapshot); 2731 const v8::HeapGraphNode* obj = 2732 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj"); 2733 CHECK_NE(NULL, obj); 2734 const v8::HeapGraphNode* map = 2735 GetProperty(obj, v8::HeapGraphEdge::kInternal, "map"); 2736 CHECK_NE(NULL, map); 2737 const v8::HeapGraphNode* dependent_code = 2738 GetProperty(map, v8::HeapGraphEdge::kInternal, "dependent_code"); 2739 if (!dependent_code) return; 2740 int count = dependent_code->GetChildrenCount(); 2741 CHECK_NE(0, count); 2742 for (int i = 0; i < count; ++i) { 2743 const v8::HeapGraphEdge* prop = dependent_code->GetChild(i); 2744 CHECK_EQ(v8::HeapGraphEdge::kWeak, prop->GetType()); 2745 } 2746 } 2747 2748 2749 static inline i::Address ToAddress(int n) { 2750 return reinterpret_cast<i::Address>(n); 2751 } 2752 2753 2754 TEST(AddressToTraceMap) { 2755 i::AddressToTraceMap map; 2756 2757 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(150))); 2758 2759 // [0x100, 0x200) -> 1 2760 map.AddRange(ToAddress(0x100), 0x100, 1U); 2761 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x50))); 2762 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x100))); 2763 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x150))); 2764 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x100 + 0x100))); 2765 CHECK_EQ(1, static_cast<int>(map.size())); 2766 2767 // [0x100, 0x200) -> 1, [0x200, 0x300) -> 2 2768 map.AddRange(ToAddress(0x200), 0x100, 2U); 2769 CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x2a0))); 2770 CHECK_EQ(2, static_cast<int>(map.size())); 2771 2772 // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2 2773 map.AddRange(ToAddress(0x180), 0x100, 3U); 2774 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x17F))); 2775 CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x280))); 2776 CHECK_EQ(3, map.GetTraceNodeId(ToAddress(0x180))); 2777 CHECK_EQ(3, static_cast<int>(map.size())); 2778 2779 // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2, 2780 // [0x400, 0x500) -> 4 2781 map.AddRange(ToAddress(0x400), 0x100, 4U); 2782 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x17F))); 2783 CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x280))); 2784 CHECK_EQ(3, map.GetTraceNodeId(ToAddress(0x180))); 2785 CHECK_EQ(4, map.GetTraceNodeId(ToAddress(0x450))); 2786 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x500))); 2787 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x350))); 2788 CHECK_EQ(4, static_cast<int>(map.size())); 2789 2790 // [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5 2791 map.AddRange(ToAddress(0x200), 0x400, 5U); 2792 CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x200))); 2793 CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x400))); 2794 CHECK_EQ(3, static_cast<int>(map.size())); 2795 2796 // [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5 2797 map.AddRange(ToAddress(0x180), 0x80, 6U); 2798 map.AddRange(ToAddress(0x180), 0x80, 7U); 2799 CHECK_EQ(7, map.GetTraceNodeId(ToAddress(0x180))); 2800 CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x200))); 2801 CHECK_EQ(3, static_cast<int>(map.size())); 2802 2803 map.Clear(); 2804 CHECK_EQ(0, static_cast<int>(map.size())); 2805 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x400))); 2806 } 2807