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