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