LCOV - code coverage report
Current view: top level - test/cctest - test-heap-profiler.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 2115 2163 97.8 %
Date: 2019-04-17 Functions: 141 162 87.0 %

          Line data    Source code
       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 <memory>
      33             : 
      34             : #include "src/v8.h"
      35             : 
      36             : #include "include/v8-profiler.h"
      37             : #include "src/api-inl.h"
      38             : #include "src/assembler-inl.h"
      39             : #include "src/base/hashmap.h"
      40             : #include "src/base/optional.h"
      41             : #include "src/collector.h"
      42             : #include "src/debug/debug.h"
      43             : #include "src/heap/heap-inl.h"
      44             : #include "src/objects-inl.h"
      45             : #include "src/profiler/allocation-tracker.h"
      46             : #include "src/profiler/heap-profiler.h"
      47             : #include "src/profiler/heap-snapshot-generator-inl.h"
      48             : #include "test/cctest/cctest.h"
      49             : 
      50             : using i::AllocationTraceNode;
      51             : using i::AllocationTraceTree;
      52             : using i::AllocationTracker;
      53             : using i::ArrayVector;
      54             : using i::SourceLocation;
      55             : using i::Vector;
      56             : using v8::base::Optional;
      57             : 
      58             : namespace {
      59             : 
      60             : class NamedEntriesDetector {
      61             :  public:
      62             :   NamedEntriesDetector()
      63           5 :       : has_A2(false), has_B2(false), has_C2(false) {
      64             :   }
      65             : 
      66       21365 :   void CheckEntry(i::HeapEntry* entry) {
      67       21365 :     if (strcmp(entry->name(), "A2") == 0) has_A2 = true;
      68       21365 :     if (strcmp(entry->name(), "B2") == 0) has_B2 = true;
      69       21365 :     if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
      70       21365 :   }
      71             : 
      72           5 :   void CheckAllReachables(i::HeapEntry* root) {
      73             :     v8::base::HashMap visited;
      74             :     std::vector<i::HeapEntry*> list;
      75           5 :     list.push_back(root);
      76           5 :     CheckEntry(root);
      77       21370 :     while (!list.empty()) {
      78       21365 :       i::HeapEntry* entry = list.back();
      79             :       list.pop_back();
      80      168215 :       for (int i = 0; i < entry->children_count(); ++i) {
      81             :         i::HeapGraphEdge* edge = entry->child(i);
      82      125490 :         if (edge->type() == i::HeapGraphEdge::kShortcut) continue;
      83       73425 :         i::HeapEntry* child = edge->to();
      84      146850 :         v8::base::HashMap::Entry* entry = visited.LookupOrInsert(
      85             :             reinterpret_cast<void*>(child),
      86             :             static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)));
      87       73425 :         if (entry->value)
      88             :           continue;
      89       21360 :         entry->value = reinterpret_cast<void*>(1);
      90       21360 :         list.push_back(child);
      91       21360 :         CheckEntry(child);
      92             :       }
      93             :     }
      94           5 :   }
      95             : 
      96             :   bool has_A2;
      97             :   bool has_B2;
      98             :   bool has_C2;
      99             : };
     100             : 
     101             : }  // namespace
     102             : 
     103             : 
     104         249 : static const v8::HeapGraphNode* GetGlobalObject(
     105             :     const v8::HeapSnapshot* snapshot) {
     106             :   // The 0th-child is (GC Roots), 1st is the user root.
     107             :   const v8::HeapGraphNode* global_obj =
     108         249 :       snapshot->GetRoot()->GetChild(1)->GetToNode();
     109         249 :   CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
     110             :       reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
     111         249 :   return global_obj;
     112             : }
     113             : 
     114             : static const char* GetName(const v8::HeapGraphNode* node) {
     115             :   return const_cast<i::HeapEntry*>(reinterpret_cast<const i::HeapEntry*>(node))
     116             :       ->name();
     117             : }
     118             : 
     119             : static const char* GetName(const v8::HeapGraphEdge* edge) {
     120             :   return const_cast<i::HeapGraphEdge*>(
     121             :              reinterpret_cast<const i::HeapGraphEdge*>(edge))
     122             :       ->name();
     123             : }
     124             : 
     125             : static size_t GetSize(const v8::HeapGraphNode* node) {
     126             :   return const_cast<i::HeapEntry*>(reinterpret_cast<const i::HeapEntry*>(node))
     127             :       ->self_size();
     128             : }
     129             : 
     130         105 : static const v8::HeapGraphNode* GetChildByName(const v8::HeapGraphNode* node,
     131             :                                                const char* name) {
     132        2777 :   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
     133        2762 :     const v8::HeapGraphNode* child = node->GetChild(i)->GetToNode();
     134        2762 :     if (!strcmp(name, GetName(child))) {
     135             :       return child;
     136             :     }
     137             :   }
     138             :   return nullptr;
     139             : }
     140             : 
     141          15 : static const v8::HeapGraphEdge* GetEdgeByChildName(
     142             :     const v8::HeapGraphNode* node, const char* name) {
     143         334 :   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
     144         334 :     const v8::HeapGraphEdge* edge = node->GetChild(i);
     145         334 :     const v8::HeapGraphNode* child = edge->GetToNode();
     146         334 :     if (!strcmp(name, GetName(child))) {
     147             :       return edge;
     148             :     }
     149             :   }
     150             :   return nullptr;
     151             : }
     152             : 
     153             : static const v8::HeapGraphNode* GetRootChild(const v8::HeapSnapshot* snapshot,
     154             :                                              const char* name) {
     155          10 :   return GetChildByName(snapshot->GetRoot(), name);
     156             : }
     157             : 
     158             : static Optional<SourceLocation> GetLocation(const v8::HeapSnapshot* s,
     159             :                                             const v8::HeapGraphNode* node) {
     160             :   const i::HeapSnapshot* snapshot = reinterpret_cast<const i::HeapSnapshot*>(s);
     161             :   const std::vector<SourceLocation>& locations = snapshot->locations();
     162             :   const i::HeapEntry* entry = reinterpret_cast<const i::HeapEntry*>(node);
     163         240 :   for (const auto& loc : locations) {
     164         480 :     if (loc.entry_index == entry->index()) {
     165             :       return Optional<SourceLocation>(loc);
     166             :     }
     167             :   }
     168             : 
     169             :   return Optional<SourceLocation>();
     170             : }
     171             : 
     172        3152 : static const v8::HeapGraphNode* GetProperty(v8::Isolate* isolate,
     173             :                                             const v8::HeapGraphNode* node,
     174             :                                             v8::HeapGraphEdge::Type type,
     175             :                                             const char* name) {
     176     6007019 :   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
     177     6006558 :     const v8::HeapGraphEdge* prop = node->GetChild(i);
     178    12010425 :     v8::String::Utf8Value prop_name(isolate, prop->GetName());
     179     6006558 :     if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
     180        2691 :       return prop->GetToNode();
     181             :   }
     182             :   return nullptr;
     183             : }
     184             : 
     185           5 : static bool HasString(v8::Isolate* isolate, const v8::HeapGraphNode* node,
     186             :                       const char* contents) {
     187           5 :   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
     188           5 :     const v8::HeapGraphEdge* prop = node->GetChild(i);
     189           5 :     const v8::HeapGraphNode* node = prop->GetToNode();
     190           5 :     if (node->GetType() == v8::HeapGraphNode::kString) {
     191          10 :       v8::String::Utf8Value node_name(isolate, node->GetName());
     192           5 :       if (strcmp(contents, *node_name) == 0) return true;
     193             :     }
     194             :   }
     195             :   return false;
     196             : }
     197             : 
     198          20 : static void EnsureNoUninstrumentedInternals(v8::Isolate* isolate,
     199             :                                             const v8::HeapGraphNode* node) {
     200         820 :   for (int i = 0; i < 20; ++i) {
     201             :     i::ScopedVector<char> buffer(10);
     202             :     const v8::HeapGraphNode* internal =
     203         400 :         GetProperty(isolate, node, v8::HeapGraphEdge::kInternal,
     204         400 :                     i::IntToCString(i, buffer));
     205         400 :     CHECK(!internal);
     206             :   }
     207          20 : }
     208             : 
     209             : // Check that snapshot has no unretained entries except root.
     210         318 : static bool ValidateSnapshot(const v8::HeapSnapshot* snapshot, int depth = 3) {
     211             :   i::HeapSnapshot* heap_snapshot = const_cast<i::HeapSnapshot*>(
     212             :       reinterpret_cast<const i::HeapSnapshot*>(snapshot));
     213             : 
     214             :   v8::base::HashMap visited;
     215             :   std::deque<i::HeapGraphEdge>& edges = heap_snapshot->edges();
     216    18253394 :   for (size_t i = 0; i < edges.size(); ++i) {
     217    27379614 :     v8::base::HashMap::Entry* entry = visited.LookupOrInsert(
     218             :         reinterpret_cast<void*>(edges[i].to()),
     219             :         static_cast<uint32_t>(reinterpret_cast<uintptr_t>(edges[i].to())));
     220             :     uint32_t ref_count = static_cast<uint32_t>(
     221     9126538 :         reinterpret_cast<uintptr_t>(entry->value));
     222     9126538 :     entry->value = reinterpret_cast<void*>(ref_count + 1);
     223             :   }
     224             :   uint32_t unretained_entries_count = 0;
     225             :   std::deque<i::HeapEntry>& entries = heap_snapshot->entries();
     226     2584715 :   for (i::HeapEntry& entry : entries) {
     227     2584397 :     v8::base::HashMap::Entry* map_entry = visited.Lookup(
     228             :         reinterpret_cast<void*>(&entry),
     229             :         static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&entry)));
     230     2584397 :     if (!map_entry && entry.id() != 1) {
     231           0 :       entry.Print("entry with no retainer", "", depth, 0);
     232           0 :       ++unretained_entries_count;
     233             :     }
     234             :   }
     235         636 :   return unretained_entries_count == 0;
     236             : }
     237             : 
     238          20 : bool EndsWith(const char* a, const char* b) {
     239          20 :   size_t length_a = strlen(a);
     240          20 :   size_t length_b = strlen(b);
     241          20 :   return (length_a >= length_b) && !strcmp(a + length_a - length_b, b);
     242             : }
     243             : 
     244       26644 : TEST(HeapSnapshot) {
     245           5 :   LocalContext env2;
     246          10 :   v8::HandleScope scope(env2->GetIsolate());
     247           5 :   v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler();
     248             : 
     249             :   CompileRun(
     250             :       "function A2() {}\n"
     251             :       "function B2(x) { return function() { return typeof x; }; }\n"
     252             :       "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
     253             :       "var a2 = new A2();\n"
     254             :       "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
     255             :       "var c2 = new C2(a2);");
     256           5 :   const v8::HeapSnapshot* snapshot_env2 = heap_profiler->TakeHeapSnapshot();
     257           5 :   CHECK(ValidateSnapshot(snapshot_env2));
     258           5 :   const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
     259             : 
     260             :   // Verify, that JS global object of env2 has '..2' properties.
     261           5 :   const v8::HeapGraphNode* a2_node = GetProperty(
     262           5 :       env2->GetIsolate(), global_env2, v8::HeapGraphEdge::kProperty, "a2");
     263           5 :   CHECK(a2_node);
     264           5 :   CHECK(GetProperty(env2->GetIsolate(), global_env2,
     265             :                     v8::HeapGraphEdge::kProperty, "b2_1"));
     266           5 :   CHECK(GetProperty(env2->GetIsolate(), global_env2,
     267             :                     v8::HeapGraphEdge::kProperty, "b2_2"));
     268           5 :   CHECK(GetProperty(env2->GetIsolate(), global_env2,
     269             :                     v8::HeapGraphEdge::kProperty, "c2"));
     270             : 
     271             :   NamedEntriesDetector det;
     272             :   det.CheckAllReachables(const_cast<i::HeapEntry*>(
     273           5 :       reinterpret_cast<const i::HeapEntry*>(global_env2)));
     274           5 :   CHECK(det.has_A2);
     275           5 :   CHECK(det.has_B2);
     276           5 :   CHECK(det.has_C2);
     277           5 : }
     278             : 
     279       26644 : TEST(HeapSnapshotLocations) {
     280           5 :   LocalContext env;
     281          10 :   v8::HandleScope scope(env->GetIsolate());
     282           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     283             : 
     284             :   CompileRun(
     285             :       "function X(a) { return function() { return a; } }\n"
     286             :       "function* getid() { yield 1; }\n"
     287             :       "class A {}\n"
     288             :       "var x = X(1);\n"
     289             :       "var g = getid();\n"
     290             :       "var o = new A();");
     291           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     292           5 :   CHECK(ValidateSnapshot(snapshot));
     293             : 
     294           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     295             :   const v8::HeapGraphNode* x =
     296           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "x");
     297           5 :   CHECK(x);
     298             : 
     299             :   Optional<SourceLocation> x_loc = GetLocation(snapshot, x);
     300           5 :   CHECK(x_loc);
     301           5 :   CHECK_EQ(0, x_loc->line);
     302           5 :   CHECK_EQ(31, x_loc->col);
     303             : 
     304             :   const v8::HeapGraphNode* g =
     305           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "g");
     306           5 :   CHECK(x);
     307             : 
     308             :   Optional<SourceLocation> g_loc = GetLocation(snapshot, g);
     309           5 :   CHECK(g_loc);
     310           5 :   CHECK_EQ(1, g_loc->line);
     311           5 :   CHECK_EQ(15, g_loc->col);
     312             : 
     313             :   const v8::HeapGraphNode* o =
     314           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "o");
     315           5 :   CHECK(x);
     316             : 
     317             :   Optional<SourceLocation> o_loc = GetLocation(snapshot, o);
     318           5 :   CHECK(o_loc);
     319           5 :   CHECK_EQ(2, o_loc->line);
     320           5 :   CHECK_EQ(0, o_loc->col);
     321           5 : }
     322             : 
     323       26644 : TEST(HeapSnapshotObjectSizes) {
     324           5 :   LocalContext env;
     325          10 :   v8::HandleScope scope(env->GetIsolate());
     326           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     327             : 
     328             :   //   -a-> X1 --a
     329             :   // x -b-> X2 <-|
     330             :   CompileRun(
     331             :       "function X(a, b) { this.a = a; this.b = b; }\n"
     332             :       "x = new X(new X(), new X());\n"
     333             :       "dummy = new X();\n"
     334             :       "(function() { x.a.a = x.b; })();");
     335           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     336           5 :   CHECK(ValidateSnapshot(snapshot));
     337           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     338             :   const v8::HeapGraphNode* x =
     339           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "x");
     340           5 :   CHECK(x);
     341             :   const v8::HeapGraphNode* x1 =
     342           5 :       GetProperty(env->GetIsolate(), x, v8::HeapGraphEdge::kProperty, "a");
     343           5 :   CHECK(x1);
     344             :   const v8::HeapGraphNode* x2 =
     345           5 :       GetProperty(env->GetIsolate(), x, v8::HeapGraphEdge::kProperty, "b");
     346           5 :   CHECK(x2);
     347             : 
     348             :   // Test sizes.
     349           5 :   CHECK_NE(0, static_cast<int>(x->GetShallowSize()));
     350           5 :   CHECK_NE(0, static_cast<int>(x1->GetShallowSize()));
     351           5 :   CHECK_NE(0, static_cast<int>(x2->GetShallowSize()));
     352           5 : }
     353             : 
     354             : 
     355       26644 : TEST(BoundFunctionInSnapshot) {
     356           5 :   LocalContext env;
     357          10 :   v8::HandleScope scope(env->GetIsolate());
     358           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     359             :   CompileRun(
     360             :       "function myFunction(a, b) { this.a = a; this.b = b; }\n"
     361             :       "function AAAAA() {}\n"
     362             :       "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
     363           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     364           5 :   CHECK(ValidateSnapshot(snapshot));
     365           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     366           5 :   const v8::HeapGraphNode* f = GetProperty(
     367           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "boundFunction");
     368           5 :   CHECK(f);
     369          20 :   CHECK(v8_str("native_bind")->Equals(env.local(), f->GetName()).FromJust());
     370           5 :   const v8::HeapGraphNode* bindings = GetProperty(
     371           5 :       env->GetIsolate(), f, v8::HeapGraphEdge::kInternal, "bindings");
     372           5 :   CHECK(bindings);
     373           5 :   CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
     374           5 :   CHECK_EQ(1, bindings->GetChildrenCount());
     375             : 
     376           5 :   const v8::HeapGraphNode* bound_this = GetProperty(
     377           5 :       env->GetIsolate(), f, v8::HeapGraphEdge::kInternal, "bound_this");
     378           5 :   CHECK(bound_this);
     379           5 :   CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());
     380             : 
     381           5 :   const v8::HeapGraphNode* bound_function = GetProperty(
     382           5 :       env->GetIsolate(), f, v8::HeapGraphEdge::kInternal, "bound_function");
     383           5 :   CHECK(bound_function);
     384           5 :   CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());
     385             : 
     386           5 :   const v8::HeapGraphNode* bound_argument = GetProperty(
     387           5 :       env->GetIsolate(), f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
     388           5 :   CHECK(bound_argument);
     389           5 :   CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
     390           5 : }
     391             : 
     392             : 
     393       26644 : TEST(HeapSnapshotEntryChildren) {
     394           5 :   LocalContext env;
     395          10 :   v8::HandleScope scope(env->GetIsolate());
     396           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     397             : 
     398             :   CompileRun(
     399             :       "function A() { }\n"
     400             :       "a = new A;");
     401           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     402           5 :   CHECK(ValidateSnapshot(snapshot));
     403           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     404         334 :   for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
     405         329 :     const v8::HeapGraphEdge* prop = global->GetChild(i);
     406         329 :     CHECK_EQ(global, prop->GetFromNode());
     407             :   }
     408             :   const v8::HeapGraphNode* a =
     409           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
     410           5 :   CHECK(a);
     411          15 :   for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
     412          10 :     const v8::HeapGraphEdge* prop = a->GetChild(i);
     413          10 :     CHECK_EQ(a, prop->GetFromNode());
     414             :   }
     415           5 : }
     416             : 
     417             : 
     418       26644 : TEST(HeapSnapshotCodeObjects) {
     419           5 :   LocalContext env;
     420          10 :   v8::HandleScope scope(env->GetIsolate());
     421           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     422             : 
     423             :   CompileRun(
     424             :       "function lazy(x) { return x - 1; }\n"
     425             :       "function compiled(x) { ()=>x; return x + 1; }\n"
     426             :       "var anonymous = (function() { return function() { return 0; } })();\n"
     427             :       "compiled(1)");
     428           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     429           5 :   CHECK(ValidateSnapshot(snapshot));
     430             : 
     431           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     432           5 :   const v8::HeapGraphNode* compiled = GetProperty(
     433           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "compiled");
     434           5 :   CHECK(compiled);
     435           5 :   CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
     436           5 :   const v8::HeapGraphNode* lazy = GetProperty(
     437           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "lazy");
     438           5 :   CHECK(lazy);
     439           5 :   CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
     440           5 :   const v8::HeapGraphNode* anonymous = GetProperty(
     441           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "anonymous");
     442           5 :   CHECK(anonymous);
     443           5 :   CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
     444          15 :   v8::String::Utf8Value anonymous_name(env->GetIsolate(), anonymous->GetName());
     445           5 :   CHECK_EQ(0, strcmp("", *anonymous_name));
     446             : 
     447             :   // Find references to shared function info.
     448           5 :   const v8::HeapGraphNode* compiled_sfi = GetProperty(
     449           5 :       env->GetIsolate(), compiled, v8::HeapGraphEdge::kInternal, "shared");
     450           5 :   CHECK(compiled_sfi);
     451           5 :   const v8::HeapGraphNode* lazy_sfi = GetProperty(
     452           5 :       env->GetIsolate(), lazy, v8::HeapGraphEdge::kInternal, "shared");
     453           5 :   CHECK(lazy_sfi);
     454             : 
     455             :   // TODO(leszeks): Check that there's bytecode on the compiled function, but
     456             :   // not the lazy function.
     457             : 
     458             :   // Verify that non-compiled function doesn't contain references to "x"
     459             :   // literal, while compiled function does. The scope info is stored in
     460             :   // FixedArray objects attached to the SharedFunctionInfo.
     461             :   bool compiled_references_x = false, lazy_references_x = false;
     462           5 :   for (int i = 0, count = compiled_sfi->GetChildrenCount(); i < count; ++i) {
     463           5 :     const v8::HeapGraphEdge* prop = compiled_sfi->GetChild(i);
     464           5 :     const v8::HeapGraphNode* node = prop->GetToNode();
     465           5 :     if (node->GetType() == v8::HeapGraphNode::kArray) {
     466           5 :       if (HasString(env->GetIsolate(), node, "x")) {
     467             :         compiled_references_x = true;
     468             :         break;
     469             :       }
     470             :     }
     471             :   }
     472          20 :   for (int i = 0, count = lazy_sfi->GetChildrenCount(); i < count; ++i) {
     473          15 :     const v8::HeapGraphEdge* prop = lazy_sfi->GetChild(i);
     474          15 :     const v8::HeapGraphNode* node = prop->GetToNode();
     475          15 :     if (node->GetType() == v8::HeapGraphNode::kArray) {
     476           0 :       if (HasString(env->GetIsolate(), node, "x")) {
     477             :         lazy_references_x = true;
     478             :         break;
     479             :       }
     480             :     }
     481             :   }
     482           5 :   CHECK(compiled_references_x);
     483           5 :   if (i::FLAG_lazy) {
     484           5 :     CHECK(!lazy_references_x);
     485             :   }
     486           5 : }
     487             : 
     488             : 
     489       26644 : TEST(HeapSnapshotHeapNumbers) {
     490           5 :   LocalContext env;
     491          10 :   v8::HandleScope scope(env->GetIsolate());
     492           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     493             :   CompileRun(
     494             :       "a = 1;    // a is Smi\n"
     495             :       "b = 2.5;  // b is HeapNumber");
     496           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     497           5 :   CHECK(ValidateSnapshot(snapshot));
     498           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     499           5 :   CHECK(!GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty,
     500             :                      "a"));
     501             :   const v8::HeapGraphNode* b =
     502           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "b");
     503           5 :   CHECK(b);
     504           5 :   CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
     505           5 : }
     506             : 
     507       26644 : TEST(HeapSnapshotHeapBigInts) {
     508           5 :   LocalContext env;
     509          10 :   v8::HandleScope scope(env->GetIsolate());
     510           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     511             :   CompileRun(
     512             :       "a = 1n;"
     513             :       "b = Object(BigInt(2))");
     514           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     515           5 :   CHECK(ValidateSnapshot(snapshot));
     516           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     517             :   const v8::HeapGraphNode* a =
     518           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
     519           5 :   CHECK(a);
     520           5 :   CHECK_EQ(v8::HeapGraphNode::kBigInt, a->GetType());
     521             :   const v8::HeapGraphNode* b =
     522           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "b");
     523           5 :   CHECK(b);
     524           5 :   CHECK_EQ(v8::HeapGraphNode::kObject, b->GetType());
     525           5 : }
     526             : 
     527       26644 : TEST(HeapSnapshotSlicedString) {
     528             :   if (!i::FLAG_string_slices) return;
     529           5 :   LocalContext env;
     530          10 :   v8::HandleScope scope(env->GetIsolate());
     531           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     532             :   CompileRun(
     533             :       "parent_string = \"123456789.123456789.123456789.123456789.123456789."
     534             :       "123456789.123456789.123456789.123456789.123456789."
     535             :       "123456789.123456789.123456789.123456789.123456789."
     536             :       "123456789.123456789.123456789.123456789.123456789."
     537             :       "123456789.123456789.123456789.123456789.123456789."
     538             :       "123456789.123456789.123456789.123456789.123456789."
     539             :       "123456789.123456789.123456789.123456789.123456789."
     540             :       "123456789.123456789.123456789.123456789.123456789.\";"
     541             :       "child_string = parent_string.slice(100);");
     542           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     543           5 :   CHECK(ValidateSnapshot(snapshot));
     544           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     545           5 :   const v8::HeapGraphNode* parent_string = GetProperty(
     546           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "parent_string");
     547           5 :   CHECK(parent_string);
     548           5 :   const v8::HeapGraphNode* child_string = GetProperty(
     549           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "child_string");
     550           5 :   CHECK(child_string);
     551           5 :   CHECK_EQ(v8::HeapGraphNode::kSlicedString, child_string->GetType());
     552           5 :   const v8::HeapGraphNode* parent = GetProperty(
     553           5 :       env->GetIsolate(), child_string, v8::HeapGraphEdge::kInternal, "parent");
     554           5 :   CHECK_EQ(parent_string, parent);
     555           5 :   heap_profiler->DeleteAllHeapSnapshots();
     556             : }
     557             : 
     558             : 
     559       26644 : TEST(HeapSnapshotConsString) {
     560           5 :   v8::Isolate* isolate = CcTest::isolate();
     561          10 :   v8::HandleScope scope(isolate);
     562             :   v8::Local<v8::ObjectTemplate> global_template =
     563           5 :       v8::ObjectTemplate::New(isolate);
     564           5 :   global_template->SetInternalFieldCount(1);
     565           5 :   LocalContext env(nullptr, global_template);
     566           5 :   v8::Local<v8::Object> global_proxy = env->Global();
     567           5 :   v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
     568           5 :   CHECK_EQ(1, global->InternalFieldCount());
     569             : 
     570             :   i::Factory* factory = CcTest::i_isolate()->factory();
     571           5 :   i::Handle<i::String> first = factory->NewStringFromStaticChars("0123456789");
     572           5 :   i::Handle<i::String> second = factory->NewStringFromStaticChars("0123456789");
     573             :   i::Handle<i::String> cons_string =
     574          10 :       factory->NewConsString(first, second).ToHandleChecked();
     575             : 
     576           5 :   global->SetInternalField(0, v8::ToApiHandle<v8::String>(cons_string));
     577             : 
     578           5 :   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
     579           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     580           5 :   CHECK(ValidateSnapshot(snapshot));
     581           5 :   const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
     582             : 
     583             :   const v8::HeapGraphNode* string_node =
     584           5 :       GetProperty(isolate, global_node, v8::HeapGraphEdge::kInternal, "0");
     585           5 :   CHECK(string_node);
     586           5 :   CHECK_EQ(v8::HeapGraphNode::kConsString, string_node->GetType());
     587             : 
     588             :   const v8::HeapGraphNode* first_node =
     589           5 :       GetProperty(isolate, string_node, v8::HeapGraphEdge::kInternal, "first");
     590           5 :   CHECK_EQ(v8::HeapGraphNode::kString, first_node->GetType());
     591             : 
     592             :   const v8::HeapGraphNode* second_node =
     593           5 :       GetProperty(isolate, string_node, v8::HeapGraphEdge::kInternal, "second");
     594           5 :   CHECK_EQ(v8::HeapGraphNode::kString, second_node->GetType());
     595             : 
     596           5 :   heap_profiler->DeleteAllHeapSnapshots();
     597           5 : }
     598             : 
     599             : 
     600       26644 : TEST(HeapSnapshotSymbol) {
     601           5 :   LocalContext env;
     602          10 :   v8::HandleScope scope(env->GetIsolate());
     603           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     604             : 
     605             :   CompileRun("a = Symbol('mySymbol');\n");
     606           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     607           5 :   CHECK(ValidateSnapshot(snapshot));
     608           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     609             :   const v8::HeapGraphNode* a =
     610           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
     611           5 :   CHECK(a);
     612           5 :   CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol);
     613          20 :   CHECK(v8_str("symbol")->Equals(env.local(), a->GetName()).FromJust());
     614             :   const v8::HeapGraphNode* name =
     615           5 :       GetProperty(env->GetIsolate(), a, v8::HeapGraphEdge::kInternal, "name");
     616           5 :   CHECK(name);
     617          20 :   CHECK(v8_str("mySymbol")->Equals(env.local(), name->GetName()).FromJust());
     618           5 : }
     619             : 
     620       26644 : TEST(HeapSnapshotWeakCollection) {
     621           5 :   LocalContext env;
     622          10 :   v8::HandleScope scope(env->GetIsolate());
     623           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     624             : 
     625             :   CompileRun(
     626             :       "k = {}; v = {}; s = 'str';\n"
     627             :       "ws = new WeakSet(); ws.add(k); ws.add(v); ws[s] = s;\n"
     628             :       "wm = new WeakMap(); wm.set(k, v); wm[s] = s;\n");
     629           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     630           5 :   CHECK(ValidateSnapshot(snapshot));
     631           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     632             :   const v8::HeapGraphNode* k =
     633           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "k");
     634           5 :   CHECK(k);
     635             :   const v8::HeapGraphNode* v =
     636           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "v");
     637           5 :   CHECK(v);
     638             :   const v8::HeapGraphNode* s =
     639           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "s");
     640           5 :   CHECK(s);
     641             : 
     642           5 :   const v8::HeapGraphNode* ws = GetProperty(env->GetIsolate(), global,
     643           5 :                                             v8::HeapGraphEdge::kProperty, "ws");
     644           5 :   CHECK(ws);
     645           5 :   CHECK_EQ(v8::HeapGraphNode::kObject, ws->GetType());
     646          20 :   CHECK(v8_str("WeakSet")->Equals(env.local(), ws->GetName()).FromJust());
     647             : 
     648             :   const v8::HeapGraphNode* ws_table =
     649           5 :       GetProperty(env->GetIsolate(), ws, v8::HeapGraphEdge::kInternal, "table");
     650           5 :   CHECK_EQ(v8::HeapGraphNode::kArray, ws_table->GetType());
     651           5 :   CHECK_GT(ws_table->GetChildrenCount(), 0);
     652             :   int weak_entries = 0;
     653          20 :   for (int i = 0, count = ws_table->GetChildrenCount(); i < count; ++i) {
     654          15 :     const v8::HeapGraphEdge* prop = ws_table->GetChild(i);
     655          15 :     if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
     656          10 :     if (k->GetId() == prop->GetToNode()->GetId()) {
     657           5 :       ++weak_entries;
     658             :     }
     659             :   }
     660           5 :   CHECK_EQ(1, weak_entries);
     661             :   const v8::HeapGraphNode* ws_s =
     662           5 :       GetProperty(env->GetIsolate(), ws, v8::HeapGraphEdge::kProperty, "str");
     663           5 :   CHECK(ws_s);
     664           5 :   CHECK_EQ(s->GetId(), ws_s->GetId());
     665             : 
     666           5 :   const v8::HeapGraphNode* wm = GetProperty(env->GetIsolate(), global,
     667           5 :                                             v8::HeapGraphEdge::kProperty, "wm");
     668           5 :   CHECK(wm);
     669           5 :   CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType());
     670          20 :   CHECK(v8_str("WeakMap")->Equals(env.local(), wm->GetName()).FromJust());
     671             : 
     672             :   const v8::HeapGraphNode* wm_table =
     673           5 :       GetProperty(env->GetIsolate(), wm, v8::HeapGraphEdge::kInternal, "table");
     674           5 :   CHECK_EQ(v8::HeapGraphNode::kArray, wm_table->GetType());
     675           5 :   CHECK_GT(wm_table->GetChildrenCount(), 0);
     676             :   weak_entries = 0;
     677          20 :   for (int i = 0, count = wm_table->GetChildrenCount(); i < count; ++i) {
     678          15 :     const v8::HeapGraphEdge* prop = wm_table->GetChild(i);
     679          15 :     if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
     680          10 :     const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
     681          10 :     if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
     682          10 :       ++weak_entries;
     683             :     }
     684             :   }
     685           5 :   CHECK_EQ(2, weak_entries);  // Key and value are weak.
     686             :   const v8::HeapGraphNode* wm_s =
     687           5 :       GetProperty(env->GetIsolate(), wm, v8::HeapGraphEdge::kProperty, "str");
     688           5 :   CHECK(wm_s);
     689           5 :   CHECK_EQ(s->GetId(), wm_s->GetId());
     690           5 : }
     691             : 
     692             : 
     693       26644 : TEST(HeapSnapshotCollection) {
     694           5 :   LocalContext env;
     695          10 :   v8::HandleScope scope(env->GetIsolate());
     696           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     697             : 
     698             :   CompileRun(
     699             :       "k = {}; v = {}; s = 'str';\n"
     700             :       "set = new Set(); set.add(k); set.add(v); set[s] = s;\n"
     701             :       "map = new Map(); map.set(k, v); map[s] = s;\n");
     702           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     703           5 :   CHECK(ValidateSnapshot(snapshot));
     704           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     705             :   const v8::HeapGraphNode* k =
     706           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "k");
     707           5 :   CHECK(k);
     708             :   const v8::HeapGraphNode* v =
     709           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "v");
     710           5 :   CHECK(v);
     711             :   const v8::HeapGraphNode* s =
     712           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "s");
     713           5 :   CHECK(s);
     714             : 
     715           5 :   const v8::HeapGraphNode* set = GetProperty(
     716           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "set");
     717           5 :   CHECK(set);
     718           5 :   CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType());
     719          20 :   CHECK(v8_str("Set")->Equals(env.local(), set->GetName()).FromJust());
     720             : 
     721           5 :   const v8::HeapGraphNode* set_table = GetProperty(
     722           5 :       env->GetIsolate(), set, v8::HeapGraphEdge::kInternal, "table");
     723           5 :   CHECK_EQ(v8::HeapGraphNode::kArray, set_table->GetType());
     724           5 :   CHECK_GT(set_table->GetChildrenCount(), 0);
     725             :   int entries = 0;
     726          20 :   for (int i = 0, count = set_table->GetChildrenCount(); i < count; ++i) {
     727          15 :     const v8::HeapGraphEdge* prop = set_table->GetChild(i);
     728          15 :     const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
     729          15 :     if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
     730          10 :       ++entries;
     731             :     }
     732             :   }
     733           5 :   CHECK_EQ(2, entries);
     734             :   const v8::HeapGraphNode* set_s =
     735           5 :       GetProperty(env->GetIsolate(), set, v8::HeapGraphEdge::kProperty, "str");
     736           5 :   CHECK(set_s);
     737           5 :   CHECK_EQ(s->GetId(), set_s->GetId());
     738             : 
     739           5 :   const v8::HeapGraphNode* map = GetProperty(
     740           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "map");
     741           5 :   CHECK(map);
     742           5 :   CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType());
     743          20 :   CHECK(v8_str("Map")->Equals(env.local(), map->GetName()).FromJust());
     744             : 
     745           5 :   const v8::HeapGraphNode* map_table = GetProperty(
     746           5 :       env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "table");
     747           5 :   CHECK_EQ(v8::HeapGraphNode::kArray, map_table->GetType());
     748           5 :   CHECK_GT(map_table->GetChildrenCount(), 0);
     749             :   entries = 0;
     750          20 :   for (int i = 0, count = map_table->GetChildrenCount(); i < count; ++i) {
     751          15 :     const v8::HeapGraphEdge* prop = map_table->GetChild(i);
     752          15 :     const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
     753          15 :     if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
     754          10 :       ++entries;
     755             :     }
     756             :   }
     757           5 :   CHECK_EQ(2, entries);
     758             :   const v8::HeapGraphNode* map_s =
     759           5 :       GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kProperty, "str");
     760           5 :   CHECK(map_s);
     761           5 :   CHECK_EQ(s->GetId(), map_s->GetId());
     762           5 : }
     763             : 
     764       26644 : TEST(HeapSnapshotMap) {
     765           5 :   LocalContext env;
     766          10 :   v8::HandleScope scope(env->GetIsolate());
     767           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     768             : 
     769             :   CompileRun(
     770             :       "function Z() { this.foo = {}; this.bar = 0; }\n"
     771             :       "z = new Z();\n");
     772           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     773           5 :   CHECK(ValidateSnapshot(snapshot));
     774           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     775             :   const v8::HeapGraphNode* z =
     776           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "z");
     777           5 :   CHECK(z);
     778             :   const v8::HeapGraphNode* map =
     779           5 :       GetProperty(env->GetIsolate(), z, v8::HeapGraphEdge::kInternal, "map");
     780           5 :   CHECK(map);
     781           5 :   CHECK(
     782             :       GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "map"));
     783           5 :   CHECK(GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal,
     784             :                     "prototype"));
     785           5 :   const v8::HeapGraphNode* parent_map = GetProperty(
     786           5 :       env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "back_pointer");
     787           5 :   CHECK(parent_map);
     788             : 
     789           5 :   CHECK(GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal,
     790             :                     "back_pointer"));
     791           5 :   CHECK(GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal,
     792             :                     "descriptors"));
     793           5 :   CHECK(GetProperty(env->GetIsolate(), parent_map, v8::HeapGraphEdge::kWeak,
     794             :                     "transition"));
     795           5 : }
     796             : 
     797       26644 : TEST(HeapSnapshotInternalReferences) {
     798           5 :   v8::Isolate* isolate = CcTest::isolate();
     799          10 :   v8::HandleScope scope(isolate);
     800             :   v8::Local<v8::ObjectTemplate> global_template =
     801           5 :       v8::ObjectTemplate::New(isolate);
     802           5 :   global_template->SetInternalFieldCount(2);
     803           5 :   LocalContext env(nullptr, global_template);
     804           5 :   v8::Local<v8::Object> global_proxy = env->Global();
     805           5 :   v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
     806           5 :   CHECK_EQ(2, global->InternalFieldCount());
     807           5 :   v8::Local<v8::Object> obj = v8::Object::New(isolate);
     808           5 :   global->SetInternalField(0, v8_num(17));
     809           5 :   global->SetInternalField(1, obj);
     810           5 :   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
     811           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     812           5 :   CHECK(ValidateSnapshot(snapshot));
     813           5 :   const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
     814             :   // The first reference will not present, because it's a Smi.
     815           5 :   CHECK(!GetProperty(env->GetIsolate(), global_node,
     816             :                      v8::HeapGraphEdge::kInternal, "0"));
     817             :   // The second reference is to an object.
     818           5 :   CHECK(GetProperty(env->GetIsolate(), global_node,
     819             :                     v8::HeapGraphEdge::kInternal, "1"));
     820           5 : }
     821             : 
     822       26644 : TEST(HeapSnapshotEphemeron) {
     823           5 :   LocalContext env;
     824          10 :   v8::HandleScope scope(env->GetIsolate());
     825           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     826             : 
     827             :   CompileRun(
     828             :       "class KeyClass{};\n"
     829             :       "class ValueClass{};\n"
     830             :       "var wm = new WeakMap();\n"
     831             :       "function foo(key) { wm.set(key, new ValueClass()); }\n"
     832             :       "var key = new KeyClass();\n"
     833             :       "foo(key);");
     834           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
     835           5 :   CHECK(ValidateSnapshot(snapshot));
     836           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
     837             : 
     838           5 :   const v8::HeapGraphNode* key = GetProperty(
     839           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "key");
     840           5 :   CHECK(key);
     841             :   bool success = false;
     842           5 :   for (int i = 0, count = key->GetChildrenCount(); i < count; ++i) {
     843           5 :     const v8::HeapGraphEdge* edge = key->GetChild(i);
     844           5 :     const v8::HeapGraphNode* child = edge->GetToNode();
     845           5 :     if (!strcmp("ValueClass", GetName(child))) {
     846          10 :       v8::String::Utf8Value edge_name(CcTest::isolate(), edge->GetName());
     847           5 :       CHECK(EndsWith(*edge_name, " / key KeyClass in WeakMap"));
     848             :       success = true;
     849             :       break;
     850             :     }
     851             :   }
     852           5 :   CHECK(success);
     853           5 : }
     854             : 
     855       26644 : TEST(HeapSnapshotAddressReuse) {
     856           5 :   LocalContext env;
     857          10 :   v8::HandleScope scope(env->GetIsolate());
     858           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     859             : 
     860             :   CompileRun(
     861             :       "function A() {}\n"
     862             :       "var a = [];\n"
     863             :       "for (var i = 0; i < 10000; ++i)\n"
     864             :       "  a[i] = new A();\n");
     865           5 :   const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
     866           5 :   CHECK(ValidateSnapshot(snapshot1));
     867           5 :   v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId();
     868             : 
     869             :   CompileRun(
     870             :       "for (var i = 0; i < 10000; ++i)\n"
     871             :       "  a[i] = new A();\n");
     872           5 :   CcTest::CollectAllGarbage();
     873             : 
     874           5 :   const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
     875           5 :   CHECK(ValidateSnapshot(snapshot2));
     876           5 :   const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
     877             : 
     878           5 :   const v8::HeapGraphNode* array_node = GetProperty(
     879           5 :       env->GetIsolate(), global2, v8::HeapGraphEdge::kProperty, "a");
     880           5 :   CHECK(array_node);
     881             :   int wrong_count = 0;
     882       50020 :   for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) {
     883       50015 :     const v8::HeapGraphEdge* prop = array_node->GetChild(i);
     884       50015 :     if (prop->GetType() != v8::HeapGraphEdge::kElement)
     885             :       continue;
     886       50000 :     v8::SnapshotObjectId id = prop->GetToNode()->GetId();
     887       50000 :     if (id < maxId1)
     888           0 :       ++wrong_count;
     889             :   }
     890           5 :   CHECK_EQ(0, wrong_count);
     891           5 : }
     892             : 
     893             : 
     894       26644 : TEST(HeapEntryIdsAndArrayShift) {
     895           5 :   LocalContext env;
     896          10 :   v8::HandleScope scope(env->GetIsolate());
     897           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     898             : 
     899             :   CompileRun(
     900             :       "function AnObject() {\n"
     901             :       "    this.first = 'first';\n"
     902             :       "    this.second = 'second';\n"
     903             :       "}\n"
     904             :       "var a = new Array();\n"
     905             :       "for (var i = 0; i < 10; ++i)\n"
     906             :       "  a.push(new AnObject());\n");
     907           5 :   const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
     908           5 :   CHECK(ValidateSnapshot(snapshot1));
     909             : 
     910             :   CompileRun(
     911             :       "for (var i = 0; i < 1; ++i)\n"
     912             :       "  a.shift();\n");
     913             : 
     914           5 :   CcTest::CollectAllGarbage();
     915             : 
     916           5 :   const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
     917           5 :   CHECK(ValidateSnapshot(snapshot2));
     918             : 
     919           5 :   const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
     920           5 :   const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
     921           5 :   CHECK_NE(0u, global1->GetId());
     922           5 :   CHECK_EQ(global1->GetId(), global2->GetId());
     923             : 
     924           5 :   const v8::HeapGraphNode* a1 = GetProperty(env->GetIsolate(), global1,
     925           5 :                                             v8::HeapGraphEdge::kProperty, "a");
     926           5 :   CHECK(a1);
     927           5 :   const v8::HeapGraphNode* k1 = GetProperty(
     928           5 :       env->GetIsolate(), a1, v8::HeapGraphEdge::kInternal, "elements");
     929           5 :   CHECK(k1);
     930           5 :   const v8::HeapGraphNode* a2 = GetProperty(env->GetIsolate(), global2,
     931           5 :                                             v8::HeapGraphEdge::kProperty, "a");
     932           5 :   CHECK(a2);
     933           5 :   const v8::HeapGraphNode* k2 = GetProperty(
     934           5 :       env->GetIsolate(), a2, v8::HeapGraphEdge::kInternal, "elements");
     935           5 :   CHECK(k2);
     936             : 
     937           5 :   CHECK_EQ(a1->GetId(), a2->GetId());
     938           5 :   CHECK_EQ(k1->GetId(), k2->GetId());
     939           5 : }
     940             : 
     941             : 
     942       26644 : TEST(HeapEntryIdsAndGC) {
     943           5 :   LocalContext env;
     944          10 :   v8::HandleScope scope(env->GetIsolate());
     945           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
     946             : 
     947             :   CompileRun(
     948             :       "function A() {}\n"
     949             :       "function B(x) { this.x = x; }\n"
     950             :       "var a = new A();\n"
     951             :       "var b = new B(a);");
     952           5 :   const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
     953           5 :   CHECK(ValidateSnapshot(snapshot1));
     954             : 
     955           5 :   CcTest::CollectAllGarbage();
     956             : 
     957           5 :   const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
     958           5 :   CHECK(ValidateSnapshot(snapshot2));
     959             : 
     960           5 :   CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000u);
     961           5 :   CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
     962             :         snapshot2->GetMaxSnapshotJSObjectId());
     963             : 
     964           5 :   const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
     965           5 :   const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
     966           5 :   CHECK_NE(0u, global1->GetId());
     967           5 :   CHECK_EQ(global1->GetId(), global2->GetId());
     968           5 :   const v8::HeapGraphNode* A1 = GetProperty(env->GetIsolate(), global1,
     969           5 :                                             v8::HeapGraphEdge::kProperty, "A");
     970           5 :   CHECK(A1);
     971           5 :   const v8::HeapGraphNode* A2 = GetProperty(env->GetIsolate(), global2,
     972           5 :                                             v8::HeapGraphEdge::kProperty, "A");
     973           5 :   CHECK(A2);
     974           5 :   CHECK_NE(0u, A1->GetId());
     975           5 :   CHECK_EQ(A1->GetId(), A2->GetId());
     976           5 :   const v8::HeapGraphNode* B1 = GetProperty(env->GetIsolate(), global1,
     977           5 :                                             v8::HeapGraphEdge::kProperty, "B");
     978           5 :   CHECK(B1);
     979           5 :   const v8::HeapGraphNode* B2 = GetProperty(env->GetIsolate(), global2,
     980           5 :                                             v8::HeapGraphEdge::kProperty, "B");
     981           5 :   CHECK(B2);
     982           5 :   CHECK_NE(0u, B1->GetId());
     983           5 :   CHECK_EQ(B1->GetId(), B2->GetId());
     984           5 :   const v8::HeapGraphNode* a1 = GetProperty(env->GetIsolate(), global1,
     985           5 :                                             v8::HeapGraphEdge::kProperty, "a");
     986           5 :   CHECK(a1);
     987           5 :   const v8::HeapGraphNode* a2 = GetProperty(env->GetIsolate(), global2,
     988           5 :                                             v8::HeapGraphEdge::kProperty, "a");
     989           5 :   CHECK(a2);
     990           5 :   CHECK_NE(0u, a1->GetId());
     991           5 :   CHECK_EQ(a1->GetId(), a2->GetId());
     992           5 :   const v8::HeapGraphNode* b1 = GetProperty(env->GetIsolate(), global1,
     993           5 :                                             v8::HeapGraphEdge::kProperty, "b");
     994           5 :   CHECK(b1);
     995           5 :   const v8::HeapGraphNode* b2 = GetProperty(env->GetIsolate(), global2,
     996           5 :                                             v8::HeapGraphEdge::kProperty, "b");
     997           5 :   CHECK(b2);
     998           5 :   CHECK_NE(0u, b1->GetId());
     999           5 :   CHECK_EQ(b1->GetId(), b2->GetId());
    1000           5 : }
    1001             : 
    1002             : namespace {
    1003             : 
    1004             : class TestJSONStream : public v8::OutputStream {
    1005             :  public:
    1006          15 :   TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
    1007             :   explicit TestJSONStream(int abort_countdown)
    1008          10 :       : eos_signaled_(0), abort_countdown_(abort_countdown) {}
    1009          20 :   ~TestJSONStream() override = default;
    1010           5 :   void EndOfStream() override { ++eos_signaled_; }
    1011        2974 :   WriteResult WriteAsciiChunk(char* buffer, int chars_written) override {
    1012        2974 :     if (abort_countdown_ > 0) --abort_countdown_;
    1013        2974 :     if (abort_countdown_ == 0) return kAbort;
    1014        2969 :     CHECK_GT(chars_written, 0);
    1015        2969 :     i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
    1016        2969 :     i::MemCopy(chunk.start(), buffer, chars_written);
    1017        2969 :     return kContinue;
    1018             :   }
    1019           0 :   virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
    1020           0 :     UNREACHABLE();
    1021             :   }
    1022           5 :   void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
    1023             :   int eos_signaled() { return eos_signaled_; }
    1024             :   int size() { return buffer_.size(); }
    1025             : 
    1026             :  private:
    1027             :   i::Collector<char> buffer_;
    1028             :   int eos_signaled_;
    1029             :   int abort_countdown_;
    1030             : };
    1031             : 
    1032          10 : class OneByteResource : public v8::String::ExternalOneByteStringResource {
    1033             :  public:
    1034           5 :   explicit OneByteResource(i::Vector<char> string) : data_(string.start()) {
    1035           5 :     length_ = string.length();
    1036             :   }
    1037     3109646 :   const char* data() const override { return data_; }
    1038          20 :   size_t length() const override { return length_; }
    1039             : 
    1040             :  private:
    1041             :   const char* data_;
    1042             :   size_t length_;
    1043             : };
    1044             : 
    1045             : }  // namespace
    1046             : 
    1047       26644 : TEST(HeapSnapshotJSONSerialization) {
    1048           5 :   v8::Isolate* isolate = CcTest::isolate();
    1049           5 :   LocalContext env;
    1050          10 :   v8::HandleScope scope(isolate);
    1051           5 :   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
    1052             : 
    1053             : #define STRING_LITERAL_FOR_TEST \
    1054             :   "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
    1055             :   CompileRun(
    1056             :       "function A(s) { this.s = s; }\n"
    1057             :       "function B(x) { this.x = x; }\n"
    1058             :       "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
    1059             :       "var b = new B(a);");
    1060           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    1061           5 :   CHECK(ValidateSnapshot(snapshot));
    1062             : 
    1063           5 :   TestJSONStream stream;
    1064           5 :   snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
    1065           5 :   CHECK_GT(stream.size(), 0);
    1066           5 :   CHECK_EQ(1, stream.eos_signaled());
    1067             :   i::ScopedVector<char> json(stream.size());
    1068             :   stream.WriteTo(json);
    1069             : 
    1070             :   // Verify that snapshot string is valid JSON.
    1071           5 :   OneByteResource* json_res = new OneByteResource(json);
    1072             :   v8::Local<v8::String> json_string =
    1073           5 :       v8::String::NewExternalOneByte(env->GetIsolate(), json_res)
    1074             :           .ToLocalChecked();
    1075          10 :   env->Global()
    1076          20 :       ->Set(env.local(), v8_str("json_snapshot"), json_string)
    1077             :       .FromJust();
    1078             :   v8::Local<v8::Value> snapshot_parse_result = CompileRun(
    1079             :       "var parsed = JSON.parse(json_snapshot); true;");
    1080           5 :   CHECK(!snapshot_parse_result.IsEmpty());
    1081             : 
    1082             :   // Verify that snapshot object has required fields.
    1083             :   v8::Local<v8::Object> parsed_snapshot =
    1084          10 :       env->Global()
    1085          15 :           ->Get(env.local(), v8_str("parsed"))
    1086             :           .ToLocalChecked()
    1087           5 :           ->ToObject(env.local())
    1088             :           .ToLocalChecked();
    1089          15 :   CHECK(parsed_snapshot->Has(env.local(), v8_str("snapshot")).FromJust());
    1090          15 :   CHECK(parsed_snapshot->Has(env.local(), v8_str("nodes")).FromJust());
    1091          15 :   CHECK(parsed_snapshot->Has(env.local(), v8_str("edges")).FromJust());
    1092          15 :   CHECK(parsed_snapshot->Has(env.local(), v8_str("locations")).FromJust());
    1093          15 :   CHECK(parsed_snapshot->Has(env.local(), v8_str("strings")).FromJust());
    1094             : 
    1095             :   // Get node and edge "member" offsets.
    1096             :   v8::Local<v8::Value> meta_analysis_result = CompileRun(
    1097             :       "var meta = parsed.snapshot.meta;\n"
    1098             :       "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n"
    1099             :       "var node_fields_count = meta.node_fields.length;\n"
    1100             :       "var edge_fields_count = meta.edge_fields.length;\n"
    1101             :       "var edge_type_offset = meta.edge_fields.indexOf('type');\n"
    1102             :       "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
    1103             :       "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
    1104             :       "var property_type ="
    1105             :       "    meta.edge_types[edge_type_offset].indexOf('property');\n"
    1106             :       "var shortcut_type ="
    1107             :       "    meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
    1108             :       "var node_count = parsed.nodes.length / node_fields_count;\n"
    1109             :       "var first_edge_indexes = parsed.first_edge_indexes = [];\n"
    1110             :       "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n"
    1111             :       "  first_edge_indexes[i] = first_edge_index;\n"
    1112             :       "  first_edge_index += edge_fields_count *\n"
    1113             :       "      parsed.nodes[i * node_fields_count + edge_count_offset];\n"
    1114             :       "}\n"
    1115             :       "first_edge_indexes[node_count] = first_edge_index;\n");
    1116           5 :   CHECK(!meta_analysis_result.IsEmpty());
    1117             : 
    1118             :   // A helper function for processing encoded nodes.
    1119             :   CompileRun(
    1120             :       "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
    1121             :       "  var nodes = parsed.nodes;\n"
    1122             :       "  var edges = parsed.edges;\n"
    1123             :       "  var strings = parsed.strings;\n"
    1124             :       "  var node_ordinal = pos / node_fields_count;\n"
    1125             :       "  for (var i = parsed.first_edge_indexes[node_ordinal],\n"
    1126             :       "      count = parsed.first_edge_indexes[node_ordinal + 1];\n"
    1127             :       "      i < count; i += edge_fields_count) {\n"
    1128             :       "    if (edges[i + edge_type_offset] === prop_type\n"
    1129             :       "        && strings[edges[i + edge_name_offset]] === prop_name)\n"
    1130             :       "      return edges[i + edge_to_node_offset];\n"
    1131             :       "  }\n"
    1132             :       "  return null;\n"
    1133             :       "}\n");
    1134             :   // Get the string index using the path: <root> -> <global>.b.x.s
    1135             :   v8::Local<v8::Value> string_obj_pos_val = CompileRun(
    1136             :       "GetChildPosByProperty(\n"
    1137             :       "  GetChildPosByProperty(\n"
    1138             :       "    GetChildPosByProperty("
    1139             :       "      parsed.edges[edge_fields_count + edge_to_node_offset],"
    1140             :       "      \"b\", property_type),\n"
    1141             :       "    \"x\", property_type),"
    1142             :       "  \"s\", property_type)");
    1143           5 :   CHECK(!string_obj_pos_val.IsEmpty());
    1144             :   int string_obj_pos = static_cast<int>(
    1145          10 :       string_obj_pos_val->ToNumber(env.local()).ToLocalChecked()->Value());
    1146             :   v8::Local<v8::Object> nodes_array =
    1147          15 :       parsed_snapshot->Get(env.local(), v8_str("nodes"))
    1148             :           .ToLocalChecked()
    1149           5 :           ->ToObject(env.local())
    1150             :           .ToLocalChecked();
    1151             :   int string_index =
    1152          10 :       static_cast<int>(nodes_array->Get(env.local(), string_obj_pos + 1)
    1153             :                            .ToLocalChecked()
    1154           5 :                            ->ToNumber(env.local())
    1155             :                            .ToLocalChecked()
    1156           5 :                            ->Value());
    1157           5 :   CHECK_GT(string_index, 0);
    1158             :   v8::Local<v8::Object> strings_array =
    1159          15 :       parsed_snapshot->Get(env.local(), v8_str("strings"))
    1160             :           .ToLocalChecked()
    1161           5 :           ->ToObject(env.local())
    1162             :           .ToLocalChecked();
    1163          10 :   v8::Local<v8::String> string = strings_array->Get(env.local(), string_index)
    1164             :                                      .ToLocalChecked()
    1165           5 :                                      ->ToString(env.local())
    1166             :                                      .ToLocalChecked();
    1167             :   v8::Local<v8::String> ref_string = CompileRun(STRING_LITERAL_FOR_TEST)
    1168           5 :                                          ->ToString(env.local())
    1169             :                                          .ToLocalChecked();
    1170             : #undef STRING_LITERAL_FOR_TEST
    1171          10 :   CHECK_EQ(0, strcmp(*v8::String::Utf8Value(env->GetIsolate(), ref_string),
    1172             :                      *v8::String::Utf8Value(env->GetIsolate(), string)));
    1173           5 : }
    1174             : 
    1175             : 
    1176       26644 : TEST(HeapSnapshotJSONSerializationAborting) {
    1177           5 :   LocalContext env;
    1178          10 :   v8::HandleScope scope(env->GetIsolate());
    1179           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1180           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    1181           5 :   CHECK(ValidateSnapshot(snapshot));
    1182             :   TestJSONStream stream(5);
    1183           5 :   snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
    1184           5 :   CHECK_GT(stream.size(), 0);
    1185           5 :   CHECK_EQ(0, stream.eos_signaled());
    1186           5 : }
    1187             : 
    1188             : namespace {
    1189             : 
    1190             : class TestStatsStream : public v8::OutputStream {
    1191             :  public:
    1192             :   TestStatsStream()
    1193             :     : eos_signaled_(0),
    1194             :       updates_written_(0),
    1195             :       entries_count_(0),
    1196             :       entries_size_(0),
    1197             :       intervals_count_(0),
    1198          55 :       first_interval_index_(-1) { }
    1199             :   TestStatsStream(const TestStatsStream& stream) V8_NOEXCEPT = default;
    1200         110 :   ~TestStatsStream() override = default;
    1201          55 :   void EndOfStream() override { ++eos_signaled_; }
    1202           0 :   WriteResult WriteAsciiChunk(char* buffer, int chars_written) override {
    1203           0 :     UNREACHABLE();
    1204             :   }
    1205          45 :   WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
    1206             :                                   int updates_written) override {
    1207          45 :     ++intervals_count_;
    1208          45 :     CHECK(updates_written);
    1209          45 :     updates_written_ += updates_written;
    1210          45 :     entries_count_ = 0;
    1211          45 :     if (first_interval_index_ == -1 && updates_written != 0)
    1212          45 :       first_interval_index_ = buffer[0].index;
    1213         145 :     for (int i = 0; i < updates_written; ++i) {
    1214          50 :       entries_count_ += buffer[i].count;
    1215          50 :       entries_size_ += buffer[i].size;
    1216             :     }
    1217             : 
    1218          45 :     return kContinue;
    1219             :   }
    1220             :   int eos_signaled() { return eos_signaled_; }
    1221             :   int updates_written() { return updates_written_; }
    1222             :   uint32_t entries_count() const { return entries_count_; }
    1223             :   uint32_t entries_size() const { return entries_size_; }
    1224             :   int intervals_count() const { return intervals_count_; }
    1225             :   int first_interval_index() const { return first_interval_index_; }
    1226             : 
    1227             :  private:
    1228             :   int eos_signaled_;
    1229             :   int updates_written_;
    1230             :   uint32_t entries_count_;
    1231             :   uint32_t entries_size_;
    1232             :   int intervals_count_;
    1233             :   int first_interval_index_;
    1234             : };
    1235             : 
    1236             : }  // namespace
    1237             : 
    1238          55 : static TestStatsStream GetHeapStatsUpdate(
    1239             :     v8::HeapProfiler* heap_profiler,
    1240             :     v8::SnapshotObjectId* object_id = nullptr) {
    1241             :   TestStatsStream stream;
    1242          55 :   int64_t timestamp = -1;
    1243             :   v8::SnapshotObjectId last_seen_id =
    1244          55 :       heap_profiler->GetHeapStats(&stream, &timestamp);
    1245          55 :   if (object_id)
    1246          20 :     *object_id = last_seen_id;
    1247          55 :   CHECK_NE(-1, timestamp);
    1248          55 :   CHECK_EQ(1, stream.eos_signaled());
    1249          55 :   return stream;
    1250             : }
    1251             : 
    1252             : 
    1253       26644 : TEST(HeapSnapshotObjectsStats) {
    1254           5 :   LocalContext env;
    1255          10 :   v8::HandleScope scope(env->GetIsolate());
    1256           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1257             : 
    1258           5 :   heap_profiler->StartTrackingHeapObjects();
    1259             :   // We have to call GC 6 times. In other case the garbage will be
    1260             :   // the reason of flakiness.
    1261          65 :   for (int i = 0; i < 6; ++i) {
    1262          30 :     CcTest::CollectAllGarbage();
    1263             :   }
    1264             : 
    1265             :   v8::SnapshotObjectId initial_id;
    1266             :   {
    1267             :     // Single chunk of data expected in update. Initial data.
    1268             :     TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
    1269           5 :                                                       &initial_id);
    1270           5 :     CHECK_EQ(1, stats_update.intervals_count());
    1271           5 :     CHECK_EQ(1, stats_update.updates_written());
    1272           5 :     CHECK_LT(0u, stats_update.entries_size());
    1273           5 :     CHECK_EQ(0, stats_update.first_interval_index());
    1274             :   }
    1275             : 
    1276             :   // No data expected in update because nothing has happened.
    1277             :   v8::SnapshotObjectId same_id;
    1278          10 :   CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written());
    1279           5 :   CHECK_EQ(initial_id, same_id);
    1280             : 
    1281             :   {
    1282             :     v8::SnapshotObjectId additional_string_id;
    1283          10 :     v8::HandleScope inner_scope_1(env->GetIsolate());
    1284           5 :     v8_str("string1");
    1285             :     {
    1286             :       // Single chunk of data with one new entry expected in update.
    1287             :       TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
    1288           5 :                                                         &additional_string_id);
    1289           5 :       CHECK_LT(same_id, additional_string_id);
    1290           5 :       CHECK_EQ(1, stats_update.intervals_count());
    1291           5 :       CHECK_EQ(1, stats_update.updates_written());
    1292           5 :       CHECK_LT(0u, stats_update.entries_size());
    1293           5 :       CHECK_EQ(1u, stats_update.entries_count());
    1294           5 :       CHECK_EQ(2, stats_update.first_interval_index());
    1295             :     }
    1296             : 
    1297             :     // No data expected in update because nothing happened.
    1298             :     v8::SnapshotObjectId last_id;
    1299          10 :     CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written());
    1300           5 :     CHECK_EQ(additional_string_id, last_id);
    1301             : 
    1302             :     {
    1303          10 :       v8::HandleScope inner_scope_2(env->GetIsolate());
    1304           5 :       v8_str("string2");
    1305             : 
    1306             :       uint32_t entries_size;
    1307             :       {
    1308          10 :         v8::HandleScope inner_scope_3(env->GetIsolate());
    1309           5 :         v8_str("string3");
    1310           5 :         v8_str("string4");
    1311             : 
    1312             :         {
    1313             :           // Single chunk of data with three new entries expected in update.
    1314           5 :           TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
    1315           5 :           CHECK_EQ(1, stats_update.intervals_count());
    1316           5 :           CHECK_EQ(1, stats_update.updates_written());
    1317           5 :           CHECK_LT(0u, entries_size = stats_update.entries_size());
    1318           5 :           CHECK_EQ(3u, stats_update.entries_count());
    1319           5 :           CHECK_EQ(4, stats_update.first_interval_index());
    1320             :         }
    1321             :       }
    1322             : 
    1323             :       {
    1324             :         // Single chunk of data with two left entries expected in update.
    1325           5 :         TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
    1326           5 :         CHECK_EQ(1, stats_update.intervals_count());
    1327           5 :         CHECK_EQ(1, stats_update.updates_written());
    1328           5 :         CHECK_GT(entries_size, stats_update.entries_size());
    1329           5 :         CHECK_EQ(1u, stats_update.entries_count());
    1330             :         // Two strings from forth interval were released.
    1331           5 :         CHECK_EQ(4, stats_update.first_interval_index());
    1332             :       }
    1333             :     }
    1334             : 
    1335             :     {
    1336             :       // Single chunk of data with 0 left entries expected in update.
    1337           5 :       TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
    1338           5 :       CHECK_EQ(1, stats_update.intervals_count());
    1339           5 :       CHECK_EQ(1, stats_update.updates_written());
    1340           5 :       CHECK_EQ(0u, stats_update.entries_size());
    1341           5 :       CHECK_EQ(0u, stats_update.entries_count());
    1342             :       // The last string from forth interval was released.
    1343           5 :       CHECK_EQ(4, stats_update.first_interval_index());
    1344             :     }
    1345             :   }
    1346             :   {
    1347             :     // Single chunk of data with 0 left entries expected in update.
    1348           5 :     TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
    1349           5 :     CHECK_EQ(1, stats_update.intervals_count());
    1350           5 :     CHECK_EQ(1, stats_update.updates_written());
    1351           5 :     CHECK_EQ(0u, stats_update.entries_size());
    1352           5 :     CHECK_EQ(0u, stats_update.entries_count());
    1353             :     // The only string from the second interval was released.
    1354           5 :     CHECK_EQ(2, stats_update.first_interval_index());
    1355             :   }
    1356             : 
    1357           5 :   v8::Local<v8::Array> array = v8::Array::New(env->GetIsolate());
    1358           5 :   CHECK_EQ(0u, array->Length());
    1359             :   // Force array's buffer allocation.
    1360          15 :   array->Set(env.local(), 2, v8_num(7)).FromJust();
    1361             : 
    1362             :   uint32_t entries_size;
    1363             :   {
    1364             :     // Single chunk of data with 2 entries expected in update.
    1365           5 :     TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
    1366           5 :     CHECK_EQ(1, stats_update.intervals_count());
    1367           5 :     CHECK_EQ(1, stats_update.updates_written());
    1368           5 :     CHECK_LT(0u, entries_size = stats_update.entries_size());
    1369             :     // They are the array and its buffer.
    1370           5 :     CHECK_EQ(2u, stats_update.entries_count());
    1371           5 :     CHECK_EQ(8, stats_update.first_interval_index());
    1372             :   }
    1373             : 
    1374        1005 :   for (int i = 0; i < 100; ++i)
    1375        1500 :     array->Set(env.local(), i, v8_num(i)).FromJust();
    1376             : 
    1377             :   {
    1378             :     // Single chunk of data with 1 entry expected in update.
    1379           5 :     TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
    1380           5 :     CHECK_EQ(1, stats_update.intervals_count());
    1381             :     // The first interval was changed because old buffer was collected.
    1382             :     // The second interval was changed because new buffer was allocated.
    1383           5 :     CHECK_EQ(2, stats_update.updates_written());
    1384           5 :     CHECK_LT(entries_size, stats_update.entries_size());
    1385           5 :     CHECK_EQ(2u, stats_update.entries_count());
    1386           5 :     CHECK_EQ(8, stats_update.first_interval_index());
    1387             :   }
    1388             : 
    1389           5 :   heap_profiler->StopTrackingHeapObjects();
    1390           5 : }
    1391             : 
    1392             : 
    1393       26644 : TEST(HeapObjectIds) {
    1394           5 :   LocalContext env;
    1395           5 :   v8::Isolate* isolate = env->GetIsolate();
    1396          10 :   v8::HandleScope scope(isolate);
    1397           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1398             : 
    1399             :   const int kLength = 10;
    1400         105 :   v8::Local<v8::Object> objects[kLength];
    1401             :   v8::SnapshotObjectId ids[kLength];
    1402             : 
    1403           5 :   heap_profiler->StartTrackingHeapObjects(false);
    1404             : 
    1405         105 :   for (int i = 0; i < kLength; i++) {
    1406          50 :     objects[i] = v8::Object::New(isolate);
    1407             :   }
    1408          10 :   GetHeapStatsUpdate(heap_profiler);
    1409             : 
    1410         105 :   for (int i = 0; i < kLength; i++) {
    1411          50 :     v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
    1412          50 :     CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
    1413          50 :     ids[i] = id;
    1414             :   }
    1415             : 
    1416           5 :   heap_profiler->StopTrackingHeapObjects();
    1417           5 :   CcTest::CollectAllAvailableGarbage();
    1418             : 
    1419         105 :   for (int i = 0; i < kLength; i++) {
    1420          50 :     v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
    1421          50 :     CHECK_EQ(ids[i], id);
    1422          50 :     v8::Local<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
    1423         100 :     CHECK(objects[i]->Equals(env.local(), obj).FromJust());
    1424             :   }
    1425             : 
    1426           5 :   heap_profiler->ClearObjectIds();
    1427         105 :   for (int i = 0; i < kLength; i++) {
    1428          50 :     v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
    1429          50 :     CHECK_EQ(v8::HeapProfiler::kUnknownObjectId, id);
    1430          50 :     v8::Local<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
    1431          50 :     CHECK(obj.IsEmpty());
    1432             :   }
    1433           5 : }
    1434             : 
    1435             : 
    1436      159916 : static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
    1437             :                              const v8::HeapGraphNode* node,
    1438             :                              int level, int max_level) {
    1439      159916 :   if (level > max_level) return;
    1440       23368 :   CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
    1441      183279 :   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
    1442      159911 :     const v8::HeapGraphEdge* prop = node->GetChild(i);
    1443             :     const v8::HeapGraphNode* child =
    1444      159911 :         snapshot->GetNodeById(prop->GetToNode()->GetId());
    1445      159911 :     CHECK_EQ(prop->GetToNode()->GetId(), child->GetId());
    1446      159911 :     CHECK_EQ(prop->GetToNode(), child);
    1447      159911 :     CheckChildrenIds(snapshot, child, level + 1, max_level);
    1448             :   }
    1449             : }
    1450             : 
    1451             : 
    1452       26644 : TEST(HeapSnapshotGetNodeById) {
    1453           5 :   LocalContext env;
    1454          10 :   v8::HandleScope scope(env->GetIsolate());
    1455           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1456             : 
    1457           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    1458           5 :   CHECK(ValidateSnapshot(snapshot));
    1459           5 :   const v8::HeapGraphNode* root = snapshot->GetRoot();
    1460           5 :   CheckChildrenIds(snapshot, root, 0, 3);
    1461             :   // Check a big id, which should not exist yet.
    1462           5 :   CHECK(!snapshot->GetNodeById(0x1000000UL));
    1463           5 : }
    1464             : 
    1465             : 
    1466       26644 : TEST(HeapSnapshotGetSnapshotObjectId) {
    1467           5 :   LocalContext env;
    1468          10 :   v8::HandleScope scope(env->GetIsolate());
    1469           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1470             :   CompileRun("globalObject = {};\n");
    1471           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    1472           5 :   CHECK(ValidateSnapshot(snapshot));
    1473           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    1474           5 :   const v8::HeapGraphNode* global_object = GetProperty(
    1475           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "globalObject");
    1476           5 :   CHECK(global_object);
    1477             : 
    1478             :   v8::Local<v8::Value> globalObjectHandle =
    1479          20 :       env->Global()->Get(env.local(), v8_str("globalObject")).ToLocalChecked();
    1480           5 :   CHECK(!globalObjectHandle.IsEmpty());
    1481           5 :   CHECK(globalObjectHandle->IsObject());
    1482             : 
    1483           5 :   v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle);
    1484           5 :   CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
    1485           5 :   CHECK_EQ(id, global_object->GetId());
    1486           5 : }
    1487             : 
    1488             : 
    1489       26644 : TEST(HeapSnapshotUnknownSnapshotObjectId) {
    1490           5 :   LocalContext env;
    1491          10 :   v8::HandleScope scope(env->GetIsolate());
    1492           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1493             :   CompileRun("globalObject = {};\n");
    1494           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    1495           5 :   CHECK(ValidateSnapshot(snapshot));
    1496             :   const v8::HeapGraphNode* node =
    1497           5 :       snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
    1498           5 :   CHECK(!node);
    1499           5 : }
    1500             : 
    1501             : 
    1502             : namespace {
    1503             : 
    1504          25 : class TestActivityControl : public v8::ActivityControl {
    1505             :  public:
    1506             :   explicit TestActivityControl(int abort_count)
    1507             :       : done_(0),
    1508             :         total_(0),
    1509             :         abort_count_(abort_count),
    1510          15 :         reported_finish_(false) {}
    1511          35 :   ControlOption ReportProgressValue(int done, int total) override {
    1512          35 :     done_ = done;
    1513          35 :     total_ = total;
    1514          35 :     CHECK_LE(done_, total_);
    1515          35 :     if (done_ == total_) {
    1516          10 :       CHECK(!reported_finish_);
    1517          10 :       reported_finish_ = true;
    1518             :     }
    1519          35 :     return --abort_count_ != 0 ? kContinue : kAbort;
    1520             :   }
    1521             :   int done() { return done_; }
    1522             :   int total() { return total_; }
    1523             : 
    1524             :  private:
    1525             :   int done_;
    1526             :   int total_;
    1527             :   int abort_count_;
    1528             :   bool reported_finish_;
    1529             : };
    1530             : 
    1531             : }  // namespace
    1532             : 
    1533             : 
    1534       26644 : TEST(TakeHeapSnapshotAborting) {
    1535           5 :   LocalContext env;
    1536          10 :   v8::HandleScope scope(env->GetIsolate());
    1537             : 
    1538           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1539           5 :   const int snapshots_count = heap_profiler->GetSnapshotCount();
    1540             :   TestActivityControl aborting_control(1);
    1541             :   const v8::HeapSnapshot* no_snapshot =
    1542           5 :       heap_profiler->TakeHeapSnapshot(&aborting_control);
    1543           5 :   CHECK(!no_snapshot);
    1544           5 :   CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount());
    1545           5 :   CHECK_GT(aborting_control.total(), aborting_control.done());
    1546             : 
    1547             :   TestActivityControl control(-1);  // Don't abort.
    1548           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(&control);
    1549           5 :   CHECK(ValidateSnapshot(snapshot));
    1550             : 
    1551           5 :   CHECK(snapshot);
    1552           5 :   CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount());
    1553           5 :   CHECK_EQ(control.total(), control.done());
    1554           5 :   CHECK_GT(control.total(), 0);
    1555           5 : }
    1556             : 
    1557       26644 : TEST(TakeHeapSnapshotReportFinishOnce) {
    1558           5 :   LocalContext env;
    1559          10 :   v8::HandleScope scope(env->GetIsolate());
    1560           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1561             :   TestActivityControl control(-1);
    1562           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(&control);
    1563           5 :   CHECK(ValidateSnapshot(snapshot));
    1564           5 :   CHECK_EQ(control.total(), control.done());
    1565           5 :   CHECK_GT(control.total(), 0);
    1566           5 : }
    1567             : 
    1568             : namespace {
    1569             : 
    1570           5 : class EmbedderGraphBuilder : public v8::PersistentHandleVisitor {
    1571             :  public:
    1572          20 :   class Node : public v8::EmbedderGraph::Node {
    1573             :    public:
    1574          10 :     Node(const char* name, size_t size) : name_(name), size_(size) {}
    1575             :     // v8::EmbedderGraph::Node
    1576          10 :     const char* Name() override { return name_; }
    1577          10 :     size_t SizeInBytes() override { return size_; }
    1578             : 
    1579             :    private:
    1580             :     const char* name_;
    1581             :     size_t size_;
    1582             :   };
    1583             : 
    1584          20 :   class Group : public Node {
    1585             :    public:
    1586          10 :     explicit Group(const char* name) : Node(name, 0) {}
    1587             :     // v8::EmbedderGraph::EmbedderNode
    1588          20 :     bool IsRootNode() override { return true; }
    1589             :   };
    1590             : 
    1591           5 :   EmbedderGraphBuilder(v8::Isolate* isolate, v8::EmbedderGraph* graph)
    1592           5 :       : isolate_(isolate), graph_(graph) {
    1593           5 :     classid_to_group_[0] = nullptr;
    1594             :     classid_to_group_[1] =
    1595          15 :         graph->AddNode(std::unique_ptr<Group>(new Group("aaa-group")));
    1596             :     classid_to_group_[2] =
    1597          15 :         graph->AddNode(std::unique_ptr<Group>(new Group("ccc-group")));
    1598           5 :   }
    1599             : 
    1600           5 :   static void BuildEmbedderGraph(v8::Isolate* isolate, v8::EmbedderGraph* graph,
    1601             :                                  void* data) {
    1602           5 :     EmbedderGraphBuilder builder(isolate, graph);
    1603           5 :     isolate->VisitHandlesWithClassIds(&builder);
    1604           5 :   }
    1605             : 
    1606          15 :   void VisitPersistentHandle(v8::Persistent<v8::Value>* value,
    1607             :                              uint16_t class_id) override {
    1608             :     v8::Local<v8::Value> wrapper = v8::Local<v8::Value>::New(
    1609          30 :         isolate_, v8::Persistent<v8::Value>::Cast(*value));
    1610          15 :     if (class_id == 1) {
    1611          10 :       if (wrapper->IsString()) {
    1612          20 :         v8::String::Utf8Value utf8(CcTest::isolate(), wrapper);
    1613             :         DCHECK(!strcmp(*utf8, "AAA") || !strcmp(*utf8, "BBB"));
    1614          10 :         v8::EmbedderGraph::Node* node = graph_->V8Node(wrapper);
    1615          10 :         v8::EmbedderGraph::Node* group = classid_to_group_[1];
    1616          10 :         graph_->AddEdge(node, group);
    1617          10 :         graph_->AddEdge(group, node);
    1618             :       }
    1619           5 :     } else if (class_id == 2) {
    1620           5 :       if (wrapper->IsString()) {
    1621          10 :         v8::String::Utf8Value utf8(CcTest::isolate(), wrapper);
    1622             :         DCHECK(!strcmp(*utf8, "CCC"));
    1623           5 :         v8::EmbedderGraph::Node* node = graph_->V8Node(wrapper);
    1624           5 :         v8::EmbedderGraph::Node* group = classid_to_group_[2];
    1625           5 :         graph_->AddEdge(node, group);
    1626           5 :         graph_->AddEdge(group, node);
    1627             :       }
    1628             :     } else {
    1629           0 :       UNREACHABLE();
    1630             :     }
    1631          15 :   }
    1632             : 
    1633             :  private:
    1634             :   v8::Isolate* isolate_;
    1635             :   v8::EmbedderGraph* graph_;
    1636             :   v8::EmbedderGraph::Node* classid_to_group_[3];
    1637             : };
    1638             : 
    1639             : }  // namespace
    1640             : 
    1641             : 
    1642          55 : static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
    1643             :                                         v8::HeapGraphNode::Type type,
    1644             :                                         const char* name) {
    1645         230 :   for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
    1646         230 :     const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
    1647         230 :     if (node->GetType() == type && strcmp(name,
    1648             :                const_cast<i::HeapEntry*>(
    1649             :                    reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
    1650             :       return node;
    1651             :     }
    1652             :   }
    1653             :   return nullptr;
    1654             : }
    1655             : 
    1656             : 
    1657       26644 : TEST(HeapSnapshotRetainedObjectInfo) {
    1658           5 :   LocalContext env;
    1659           5 :   v8::Isolate* isolate = env->GetIsolate();
    1660          10 :   v8::HandleScope scope(isolate);
    1661           5 :   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
    1662             : 
    1663             :   heap_profiler->AddBuildEmbedderGraphCallback(
    1664           5 :       EmbedderGraphBuilder::BuildEmbedderGraph, nullptr);
    1665           5 :   v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
    1666             :   p_AAA.SetWrapperClassId(1);
    1667           5 :   v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
    1668             :   p_BBB.SetWrapperClassId(1);
    1669           5 :   v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC"));
    1670             :   p_CCC.SetWrapperClassId(2);
    1671           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    1672           5 :   CHECK(ValidateSnapshot(snapshot));
    1673             : 
    1674             :   const v8::HeapGraphNode* native_group_aaa =
    1675           5 :       GetNode(snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
    1676           5 :   CHECK_NOT_NULL(native_group_aaa);
    1677             :   const v8::HeapGraphNode* native_group_ccc =
    1678           5 :       GetNode(snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
    1679           5 :   CHECK_NOT_NULL(native_group_ccc);
    1680             : 
    1681             :   const v8::HeapGraphNode* n_AAA =
    1682           5 :       GetNode(native_group_aaa, v8::HeapGraphNode::kString, "AAA");
    1683           5 :   CHECK(n_AAA);
    1684             :   const v8::HeapGraphNode* n_BBB =
    1685           5 :       GetNode(native_group_aaa, v8::HeapGraphNode::kString, "BBB");
    1686           5 :   CHECK(n_BBB);
    1687             :   const v8::HeapGraphNode* n_CCC =
    1688           5 :       GetNode(native_group_ccc, v8::HeapGraphNode::kString, "CCC");
    1689           5 :   CHECK(n_CCC);
    1690             : 
    1691           5 :   CHECK_EQ(native_group_aaa, GetChildByName(n_AAA, "aaa-group"));
    1692           5 :   CHECK_EQ(native_group_aaa, GetChildByName(n_BBB, "aaa-group"));
    1693           5 :   CHECK_EQ(native_group_ccc, GetChildByName(n_CCC, "ccc-group"));
    1694           5 : }
    1695             : 
    1696       26644 : TEST(DeleteAllHeapSnapshots) {
    1697           5 :   LocalContext env;
    1698          10 :   v8::HandleScope scope(env->GetIsolate());
    1699           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1700             : 
    1701           5 :   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
    1702           5 :   heap_profiler->DeleteAllHeapSnapshots();
    1703           5 :   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
    1704           5 :   CHECK(heap_profiler->TakeHeapSnapshot());
    1705           5 :   CHECK_EQ(1, heap_profiler->GetSnapshotCount());
    1706           5 :   heap_profiler->DeleteAllHeapSnapshots();
    1707           5 :   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
    1708           5 :   CHECK(heap_profiler->TakeHeapSnapshot());
    1709           5 :   CHECK(heap_profiler->TakeHeapSnapshot());
    1710           5 :   CHECK_EQ(2, heap_profiler->GetSnapshotCount());
    1711           5 :   heap_profiler->DeleteAllHeapSnapshots();
    1712           5 :   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
    1713           5 : }
    1714             : 
    1715             : 
    1716          35 : static bool FindHeapSnapshot(v8::HeapProfiler* profiler,
    1717             :                              const v8::HeapSnapshot* snapshot) {
    1718          35 :   int length = profiler->GetSnapshotCount();
    1719          55 :   for (int i = 0; i < length; i++) {
    1720          30 :     if (snapshot == profiler->GetHeapSnapshot(i)) return true;
    1721             :   }
    1722             :   return false;
    1723             : }
    1724             : 
    1725             : 
    1726       26644 : TEST(DeleteHeapSnapshot) {
    1727           5 :   LocalContext env;
    1728          10 :   v8::HandleScope scope(env->GetIsolate());
    1729           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1730             : 
    1731           5 :   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
    1732           5 :   const v8::HeapSnapshot* s1 = heap_profiler->TakeHeapSnapshot();
    1733             : 
    1734           5 :   CHECK(s1);
    1735           5 :   CHECK_EQ(1, heap_profiler->GetSnapshotCount());
    1736           5 :   CHECK(FindHeapSnapshot(heap_profiler, s1));
    1737           5 :   const_cast<v8::HeapSnapshot*>(s1)->Delete();
    1738           5 :   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
    1739           5 :   CHECK(!FindHeapSnapshot(heap_profiler, s1));
    1740             : 
    1741           5 :   const v8::HeapSnapshot* s2 = heap_profiler->TakeHeapSnapshot();
    1742           5 :   CHECK(s2);
    1743           5 :   CHECK_EQ(1, heap_profiler->GetSnapshotCount());
    1744           5 :   CHECK(FindHeapSnapshot(heap_profiler, s2));
    1745           5 :   const v8::HeapSnapshot* s3 = heap_profiler->TakeHeapSnapshot();
    1746           5 :   CHECK(s3);
    1747           5 :   CHECK_EQ(2, heap_profiler->GetSnapshotCount());
    1748           5 :   CHECK_NE(s2, s3);
    1749           5 :   CHECK(FindHeapSnapshot(heap_profiler, s3));
    1750           5 :   const_cast<v8::HeapSnapshot*>(s2)->Delete();
    1751           5 :   CHECK_EQ(1, heap_profiler->GetSnapshotCount());
    1752           5 :   CHECK(!FindHeapSnapshot(heap_profiler, s2));
    1753           5 :   CHECK(FindHeapSnapshot(heap_profiler, s3));
    1754           5 :   const_cast<v8::HeapSnapshot*>(s3)->Delete();
    1755           5 :   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
    1756           5 :   CHECK(!FindHeapSnapshot(heap_profiler, s3));
    1757           5 : }
    1758             : 
    1759             : 
    1760          10 : class NameResolver : public v8::HeapProfiler::ObjectNameResolver {
    1761             :  public:
    1762           5 :   const char* GetName(v8::Local<v8::Object> object) override {
    1763           5 :     return "Global object name";
    1764             :   }
    1765             : };
    1766             : 
    1767             : 
    1768       26644 : TEST(GlobalObjectName) {
    1769           5 :   LocalContext env;
    1770          10 :   v8::HandleScope scope(env->GetIsolate());
    1771           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1772             : 
    1773             :   CompileRun("document = { URL:\"abcdefgh\" };");
    1774             : 
    1775           5 :   NameResolver name_resolver;
    1776             :   const v8::HeapSnapshot* snapshot =
    1777           5 :       heap_profiler->TakeHeapSnapshot(nullptr, &name_resolver);
    1778           5 :   CHECK(ValidateSnapshot(snapshot));
    1779           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    1780           5 :   CHECK(global);
    1781           5 :   CHECK_EQ(0,
    1782             :            strcmp("Object / Global object name",
    1783             :                   const_cast<i::HeapEntry*>(
    1784             :                       reinterpret_cast<const i::HeapEntry*>(global))->name()));
    1785           5 : }
    1786             : 
    1787             : 
    1788       26644 : TEST(GlobalObjectFields) {
    1789           5 :   LocalContext env;
    1790          10 :   v8::HandleScope scope(env->GetIsolate());
    1791           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1792             :   CompileRun("obj = {};");
    1793           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    1794           5 :   CHECK(ValidateSnapshot(snapshot));
    1795           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    1796             :   const v8::HeapGraphNode* native_context =
    1797           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kInternal,
    1798           5 :                   "native_context");
    1799           5 :   CHECK(native_context);
    1800           5 :   const v8::HeapGraphNode* global_proxy = GetProperty(
    1801           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kInternal, "global_proxy");
    1802           5 :   CHECK(global_proxy);
    1803           5 : }
    1804             : 
    1805             : 
    1806       26644 : TEST(NoHandleLeaks) {
    1807           5 :   LocalContext env;
    1808          10 :   v8::HandleScope scope(env->GetIsolate());
    1809           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1810             : 
    1811             :   CompileRun("document = { URL:\"abcdefgh\" };");
    1812             : 
    1813             :   i::Isolate* isolate = CcTest::i_isolate();
    1814           5 :   int count_before = i::HandleScope::NumberOfHandles(isolate);
    1815           5 :   heap_profiler->TakeHeapSnapshot();
    1816           5 :   int count_after = i::HandleScope::NumberOfHandles(isolate);
    1817           5 :   CHECK_EQ(count_before, count_after);
    1818           5 : }
    1819             : 
    1820             : 
    1821       26644 : TEST(NodesIteration) {
    1822           5 :   LocalContext env;
    1823          10 :   v8::HandleScope scope(env->GetIsolate());
    1824           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1825           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    1826           5 :   CHECK(ValidateSnapshot(snapshot));
    1827           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    1828           5 :   CHECK(global);
    1829             :   // Verify that we can find this object by iteration.
    1830           5 :   const int nodes_count = snapshot->GetNodesCount();
    1831             :   int count = 0;
    1832       67341 :   for (int i = 0; i < nodes_count; ++i) {
    1833       33668 :     if (snapshot->GetNode(i) == global)
    1834           5 :       ++count;
    1835             :   }
    1836           5 :   CHECK_EQ(1, count);
    1837           5 : }
    1838             : 
    1839             : 
    1840       26644 : TEST(GetHeapValueForNode) {
    1841           5 :   LocalContext env;
    1842          10 :   v8::HandleScope scope(env->GetIsolate());
    1843           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1844             : 
    1845             :   CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
    1846           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    1847           5 :   CHECK(ValidateSnapshot(snapshot));
    1848           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    1849          10 :   CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject());
    1850             :   v8::Local<v8::Object> js_global =
    1851          10 :       env->Global()->GetPrototype().As<v8::Object>();
    1852          10 :   CHECK_EQ(js_global, heap_profiler->FindObjectById(global->GetId()));
    1853             :   const v8::HeapGraphNode* obj =
    1854           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
    1855          10 :   CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject());
    1856          15 :   v8::Local<v8::Object> js_obj = js_global->Get(env.local(), v8_str("a"))
    1857             :                                      .ToLocalChecked()
    1858             :                                      .As<v8::Object>();
    1859          10 :   CHECK_EQ(js_obj, heap_profiler->FindObjectById(obj->GetId()));
    1860           5 :   const v8::HeapGraphNode* s_prop = GetProperty(
    1861           5 :       env->GetIsolate(), obj, v8::HeapGraphEdge::kProperty, "s_prop");
    1862          15 :   v8::Local<v8::String> js_s_prop = js_obj->Get(env.local(), v8_str("s_prop"))
    1863             :                                         .ToLocalChecked()
    1864             :                                         .As<v8::String>();
    1865          10 :   CHECK_EQ(js_s_prop, heap_profiler->FindObjectById(s_prop->GetId()));
    1866           5 :   const v8::HeapGraphNode* n_prop = GetProperty(
    1867           5 :       env->GetIsolate(), obj, v8::HeapGraphEdge::kProperty, "n_prop");
    1868          15 :   v8::Local<v8::String> js_n_prop = js_obj->Get(env.local(), v8_str("n_prop"))
    1869             :                                         .ToLocalChecked()
    1870             :                                         .As<v8::String>();
    1871          10 :   CHECK_EQ(js_n_prop, heap_profiler->FindObjectById(n_prop->GetId()));
    1872           5 : }
    1873             : 
    1874             : 
    1875       26644 : TEST(GetHeapValueForDeletedObject) {
    1876           5 :   LocalContext env;
    1877          10 :   v8::HandleScope scope(env->GetIsolate());
    1878           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    1879             : 
    1880             :   // It is impossible to delete a global property, so we are about to delete a
    1881             :   // property of the "a" object. Also, the "p" object can't be an empty one
    1882             :   // because the empty object is static and isn't actually deleted.
    1883             :   CompileRun("a = { p: { r: {} } };");
    1884           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    1885           5 :   CHECK(ValidateSnapshot(snapshot));
    1886           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    1887             :   const v8::HeapGraphNode* obj =
    1888           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
    1889             :   const v8::HeapGraphNode* prop =
    1890           5 :       GetProperty(env->GetIsolate(), obj, v8::HeapGraphEdge::kProperty, "p");
    1891             :   {
    1892             :     // Perform the check inside a nested local scope to avoid creating a
    1893             :     // reference to the object we are deleting.
    1894          10 :     v8::HandleScope scope(env->GetIsolate());
    1895          10 :     CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject());
    1896             :   }
    1897             :   CompileRun("delete a.p;");
    1898          10 :   CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty());
    1899           5 : }
    1900             : 
    1901          30 : static int StringCmp(const char* ref, i::String act) {
    1902          30 :   std::unique_ptr<char[]> s_act = act->ToCString();
    1903          30 :   int result = strcmp(ref, s_act.get());
    1904          30 :   if (result != 0)
    1905           0 :     fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get());
    1906          30 :   return result;
    1907             : }
    1908             : 
    1909       26644 : TEST(GetConstructor) {
    1910           5 :   LocalContext env;
    1911          10 :   v8::HandleScope scope(env->GetIsolate());
    1912             : 
    1913             :   CompileRun(
    1914             :       "function Constructor1() {};\n"
    1915             :       "var obj1 = new Constructor1();\n"
    1916             :       "var Constructor2 = function() {};\n"
    1917             :       "var obj2 = new Constructor2();\n"
    1918             :       "var obj3 = {};\n"
    1919             :       "obj3.__proto__ = { constructor: function Constructor3() {} };\n"
    1920             :       "var obj4 = {};\n"
    1921             :       "// Slow properties\n"
    1922             :       "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
    1923             :       "obj4.__proto__ = { constructor: function Constructor4() {} };\n"
    1924             :       "var obj5 = {};\n"
    1925             :       "var obj6 = {};\n"
    1926             :       "obj6.constructor = 6;");
    1927             :   v8::Local<v8::Object> js_global =
    1928          10 :       env->Global()->GetPrototype().As<v8::Object>();
    1929          15 :   v8::Local<v8::Object> obj1 = js_global->Get(env.local(), v8_str("obj1"))
    1930             :                                    .ToLocalChecked()
    1931             :                                    .As<v8::Object>();
    1932             :   i::Handle<i::JSObject> js_obj1 =
    1933             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj1));
    1934          10 :   CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj1).is_null());
    1935          15 :   v8::Local<v8::Object> obj2 = js_global->Get(env.local(), v8_str("obj2"))
    1936             :                                    .ToLocalChecked()
    1937             :                                    .As<v8::Object>();
    1938             :   i::Handle<i::JSObject> js_obj2 =
    1939             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj2));
    1940          10 :   CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj2).is_null());
    1941          15 :   v8::Local<v8::Object> obj3 = js_global->Get(env.local(), v8_str("obj3"))
    1942             :                                    .ToLocalChecked()
    1943             :                                    .As<v8::Object>();
    1944             :   i::Handle<i::JSObject> js_obj3 =
    1945             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj3));
    1946          10 :   CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj3).is_null());
    1947          15 :   v8::Local<v8::Object> obj4 = js_global->Get(env.local(), v8_str("obj4"))
    1948             :                                    .ToLocalChecked()
    1949             :                                    .As<v8::Object>();
    1950             :   i::Handle<i::JSObject> js_obj4 =
    1951             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj4));
    1952          10 :   CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj4).is_null());
    1953          15 :   v8::Local<v8::Object> obj5 = js_global->Get(env.local(), v8_str("obj5"))
    1954             :                                    .ToLocalChecked()
    1955             :                                    .As<v8::Object>();
    1956             :   i::Handle<i::JSObject> js_obj5 =
    1957             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj5));
    1958          10 :   CHECK(i::V8HeapExplorer::GetConstructor(*js_obj5).is_null());
    1959          15 :   v8::Local<v8::Object> obj6 = js_global->Get(env.local(), v8_str("obj6"))
    1960             :                                    .ToLocalChecked()
    1961             :                                    .As<v8::Object>();
    1962             :   i::Handle<i::JSObject> js_obj6 =
    1963             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj6));
    1964          10 :   CHECK(i::V8HeapExplorer::GetConstructor(*js_obj6).is_null());
    1965           5 : }
    1966             : 
    1967       26644 : TEST(GetConstructorName) {
    1968           5 :   LocalContext env;
    1969          10 :   v8::HandleScope scope(env->GetIsolate());
    1970             : 
    1971             :   CompileRun(
    1972             :       "function Constructor1() {};\n"
    1973             :       "var obj1 = new Constructor1();\n"
    1974             :       "var Constructor2 = function() {};\n"
    1975             :       "var obj2 = new Constructor2();\n"
    1976             :       "var obj3 = {};\n"
    1977             :       "obj3.__proto__ = { constructor: function Constructor3() {} };\n"
    1978             :       "var obj4 = {};\n"
    1979             :       "// Slow properties\n"
    1980             :       "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
    1981             :       "obj4.__proto__ = { constructor: function Constructor4() {} };\n"
    1982             :       "var obj5 = {};\n"
    1983             :       "var obj6 = {};\n"
    1984             :       "obj6.constructor = 6;");
    1985             :   v8::Local<v8::Object> js_global =
    1986          10 :       env->Global()->GetPrototype().As<v8::Object>();
    1987          15 :   v8::Local<v8::Object> obj1 = js_global->Get(env.local(), v8_str("obj1"))
    1988             :                                    .ToLocalChecked()
    1989             :                                    .As<v8::Object>();
    1990             :   i::Handle<i::JSObject> js_obj1 =
    1991             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj1));
    1992           5 :   CHECK_EQ(0, StringCmp(
    1993             :       "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
    1994          15 :   v8::Local<v8::Object> obj2 = js_global->Get(env.local(), v8_str("obj2"))
    1995             :                                    .ToLocalChecked()
    1996             :                                    .As<v8::Object>();
    1997             :   i::Handle<i::JSObject> js_obj2 =
    1998             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj2));
    1999           5 :   CHECK_EQ(0, StringCmp(
    2000             :       "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
    2001          15 :   v8::Local<v8::Object> obj3 = js_global->Get(env.local(), v8_str("obj3"))
    2002             :                                    .ToLocalChecked()
    2003             :                                    .As<v8::Object>();
    2004             :   i::Handle<i::JSObject> js_obj3 =
    2005             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj3));
    2006           5 :   CHECK_EQ(0, StringCmp("Constructor3",
    2007             :                         i::V8HeapExplorer::GetConstructorName(*js_obj3)));
    2008          15 :   v8::Local<v8::Object> obj4 = js_global->Get(env.local(), v8_str("obj4"))
    2009             :                                    .ToLocalChecked()
    2010             :                                    .As<v8::Object>();
    2011             :   i::Handle<i::JSObject> js_obj4 =
    2012             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj4));
    2013           5 :   CHECK_EQ(0, StringCmp("Constructor4",
    2014             :                         i::V8HeapExplorer::GetConstructorName(*js_obj4)));
    2015          15 :   v8::Local<v8::Object> obj5 = js_global->Get(env.local(), v8_str("obj5"))
    2016             :                                    .ToLocalChecked()
    2017             :                                    .As<v8::Object>();
    2018             :   i::Handle<i::JSObject> js_obj5 =
    2019             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj5));
    2020           5 :   CHECK_EQ(0, StringCmp(
    2021             :       "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
    2022          15 :   v8::Local<v8::Object> obj6 = js_global->Get(env.local(), v8_str("obj6"))
    2023             :                                    .ToLocalChecked()
    2024             :                                    .As<v8::Object>();
    2025             :   i::Handle<i::JSObject> js_obj6 =
    2026             :       i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj6));
    2027           5 :   CHECK_EQ(0, StringCmp(
    2028             :       "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
    2029           5 : }
    2030             : 
    2031             : 
    2032       26644 : TEST(FastCaseAccessors) {
    2033           5 :   LocalContext env;
    2034          10 :   v8::HandleScope scope(env->GetIsolate());
    2035           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2036             : 
    2037             :   CompileRun("var obj1 = {};\n"
    2038             :              "obj1.__defineGetter__('propWithGetter', function Y() {\n"
    2039             :              "  return 42;\n"
    2040             :              "});\n"
    2041             :              "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
    2042             :              "  return this.value_ = value;\n"
    2043             :              "});\n");
    2044           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2045           5 :   CHECK(ValidateSnapshot(snapshot));
    2046             : 
    2047           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2048           5 :   CHECK(global);
    2049           5 :   const v8::HeapGraphNode* obj1 = GetProperty(
    2050           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj1");
    2051           5 :   CHECK(obj1);
    2052             :   const v8::HeapGraphNode* func;
    2053           5 :   func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
    2054           5 :                      "get propWithGetter");
    2055           5 :   CHECK(func);
    2056           5 :   func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
    2057           5 :                      "set propWithGetter");
    2058           5 :   CHECK(!func);
    2059           5 :   func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
    2060           5 :                      "set propWithSetter");
    2061           5 :   CHECK(func);
    2062           5 :   func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
    2063           5 :                      "get propWithSetter");
    2064           5 :   CHECK(!func);
    2065           5 : }
    2066             : 
    2067             : 
    2068       26644 : TEST(FastCaseRedefinedAccessors) {
    2069           5 :   LocalContext env;
    2070          10 :   v8::HandleScope scope(env->GetIsolate());
    2071           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2072             : 
    2073             :   CompileRun(
    2074             :       "var obj1 = {};\n"
    2075             :       "Object.defineProperty(obj1, 'prop', { "
    2076             :       "  get: function() { return 42; },\n"
    2077             :       "  set: function(value) { return this.prop_ = value; },\n"
    2078             :       "  configurable: true,\n"
    2079             :       "  enumerable: true,\n"
    2080             :       "});\n"
    2081             :       "Object.defineProperty(obj1, 'prop', { "
    2082             :       "  get: function() { return 153; },\n"
    2083             :       "  set: function(value) { return this.prop_ = value; },\n"
    2084             :       "  configurable: true,\n"
    2085             :       "  enumerable: true,\n"
    2086             :       "});\n");
    2087             :   v8::Local<v8::Object> js_global =
    2088          10 :       env->Global()->GetPrototype().As<v8::Object>();
    2089             :   i::Handle<i::JSReceiver> js_obj1 =
    2090          15 :       v8::Utils::OpenHandle(*js_global->Get(env.local(), v8_str("obj1"))
    2091             :                                  .ToLocalChecked()
    2092             :                                  .As<v8::Object>());
    2093             :   USE(js_obj1);
    2094             : 
    2095           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2096           5 :   CHECK(ValidateSnapshot(snapshot));
    2097           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2098           5 :   CHECK(global);
    2099           5 :   const v8::HeapGraphNode* obj1 = GetProperty(
    2100           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj1");
    2101           5 :   CHECK(obj1);
    2102             :   const v8::HeapGraphNode* func;
    2103           5 :   func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
    2104           5 :                      "get prop");
    2105           5 :   CHECK(func);
    2106           5 :   func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
    2107           5 :                      "set prop");
    2108           5 :   CHECK(func);
    2109           5 : }
    2110             : 
    2111             : 
    2112       26644 : TEST(SlowCaseAccessors) {
    2113           5 :   LocalContext env;
    2114          10 :   v8::HandleScope scope(env->GetIsolate());
    2115           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2116             : 
    2117             :   CompileRun("var obj1 = {};\n"
    2118             :              "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};"
    2119             :              "obj1.__defineGetter__('propWithGetter', function Y() {\n"
    2120             :              "  return 42;\n"
    2121             :              "});\n"
    2122             :              "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
    2123             :              "  return this.value_ = value;\n"
    2124             :              "});\n");
    2125           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2126           5 :   CHECK(ValidateSnapshot(snapshot));
    2127             : 
    2128           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2129           5 :   CHECK(global);
    2130           5 :   const v8::HeapGraphNode* obj1 = GetProperty(
    2131           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj1");
    2132           5 :   CHECK(obj1);
    2133             :   const v8::HeapGraphNode* func;
    2134           5 :   func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
    2135           5 :                      "get propWithGetter");
    2136           5 :   CHECK(func);
    2137           5 :   func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
    2138           5 :                      "set propWithGetter");
    2139           5 :   CHECK(!func);
    2140           5 :   func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
    2141           5 :                      "set propWithSetter");
    2142           5 :   CHECK(func);
    2143           5 :   func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
    2144           5 :                      "get propWithSetter");
    2145           5 :   CHECK(!func);
    2146           5 : }
    2147             : 
    2148             : 
    2149       26644 : TEST(HiddenPropertiesFastCase) {
    2150           5 :   v8::Isolate* isolate = CcTest::isolate();
    2151           5 :   LocalContext env;
    2152          10 :   v8::HandleScope scope(isolate);
    2153           5 :   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
    2154             : 
    2155             :   CompileRun(
    2156             :       "function C(x) { this.a = this; this.b = x; }\n"
    2157             :       "c = new C(2012);\n");
    2158           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2159           5 :   CHECK(ValidateSnapshot(snapshot));
    2160           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2161             :   const v8::HeapGraphNode* c =
    2162           5 :       GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "c");
    2163           5 :   CHECK(c);
    2164             :   const v8::HeapGraphNode* hidden_props =
    2165           5 :       GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "<symbol>");
    2166           5 :   CHECK(!hidden_props);
    2167             : 
    2168             :   v8::Local<v8::Value> cHandle =
    2169          20 :       env->Global()->Get(env.local(), v8_str("c")).ToLocalChecked();
    2170           5 :   CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
    2171           5 :   cHandle->ToObject(env.local())
    2172             :       .ToLocalChecked()
    2173          10 :       ->SetPrivate(env.local(),
    2174             :                    v8::Private::ForApi(env->GetIsolate(), v8_str("key")),
    2175          20 :                    v8_str("val"))
    2176             :       .FromJust();
    2177             : 
    2178           5 :   snapshot = heap_profiler->TakeHeapSnapshot();
    2179           5 :   CHECK(ValidateSnapshot(snapshot));
    2180           5 :   global = GetGlobalObject(snapshot);
    2181           5 :   c = GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "c");
    2182           5 :   CHECK(c);
    2183             :   hidden_props =
    2184           5 :       GetProperty(isolate, c, v8::HeapGraphEdge::kProperty, "<symbol>");
    2185           5 :   CHECK(hidden_props);
    2186           5 : }
    2187             : 
    2188             : 
    2189       26644 : TEST(AccessorInfo) {
    2190           5 :   LocalContext env;
    2191          10 :   v8::HandleScope scope(env->GetIsolate());
    2192           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2193             : 
    2194             :   CompileRun("function foo(x) { }\n");
    2195           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2196           5 :   CHECK(ValidateSnapshot(snapshot));
    2197           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2198           5 :   const v8::HeapGraphNode* foo = GetProperty(
    2199           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "foo");
    2200           5 :   CHECK(foo);
    2201             :   const v8::HeapGraphNode* map =
    2202           5 :       GetProperty(env->GetIsolate(), foo, v8::HeapGraphEdge::kInternal, "map");
    2203           5 :   CHECK(map);
    2204           5 :   const v8::HeapGraphNode* descriptors = GetProperty(
    2205           5 :       env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "descriptors");
    2206           5 :   CHECK(descriptors);
    2207           5 :   const v8::HeapGraphNode* length_name = GetProperty(
    2208           5 :       env->GetIsolate(), descriptors, v8::HeapGraphEdge::kInternal, "0");
    2209           5 :   CHECK(length_name);
    2210          10 :   CHECK_EQ(0, strcmp("length", *v8::String::Utf8Value(env->GetIsolate(),
    2211             :                                                       length_name->GetName())));
    2212           5 :   const v8::HeapGraphNode* length_accessor = GetProperty(
    2213           5 :       env->GetIsolate(), descriptors, v8::HeapGraphEdge::kInternal, "2");
    2214           5 :   CHECK(length_accessor);
    2215          10 :   CHECK_EQ(0, strcmp("system / AccessorInfo",
    2216             :                      *v8::String::Utf8Value(env->GetIsolate(),
    2217             :                                             length_accessor->GetName())));
    2218           5 :   const v8::HeapGraphNode* name = GetProperty(
    2219           5 :       env->GetIsolate(), length_accessor, v8::HeapGraphEdge::kInternal, "name");
    2220           5 :   CHECK(name);
    2221             :   const v8::HeapGraphNode* getter =
    2222           5 :       GetProperty(env->GetIsolate(), length_accessor,
    2223           5 :                   v8::HeapGraphEdge::kInternal, "getter");
    2224           5 :   CHECK(getter);
    2225             :   const v8::HeapGraphNode* setter =
    2226           5 :       GetProperty(env->GetIsolate(), length_accessor,
    2227           5 :                   v8::HeapGraphEdge::kInternal, "setter");
    2228           5 :   CHECK(setter);
    2229           5 : }
    2230             : 
    2231       26644 : TEST(JSGeneratorObject) {
    2232           5 :   v8::Isolate* isolate = CcTest::isolate();
    2233           5 :   LocalContext env;
    2234          10 :   v8::HandleScope scope(isolate);
    2235           5 :   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
    2236             : 
    2237             :   CompileRun(
    2238             :       "function* foo() { yield 1; }\n"
    2239             :       "g = foo();\n");
    2240           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2241           5 :   CHECK(ValidateSnapshot(snapshot));
    2242           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2243             :   const v8::HeapGraphNode* g =
    2244           5 :       GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "g");
    2245           5 :   CHECK(g);
    2246           5 :   const v8::HeapGraphNode* function = GetProperty(
    2247           5 :       env->GetIsolate(), g, v8::HeapGraphEdge::kInternal, "function");
    2248           5 :   CHECK(function);
    2249           5 :   const v8::HeapGraphNode* context = GetProperty(
    2250           5 :       env->GetIsolate(), g, v8::HeapGraphEdge::kInternal, "context");
    2251           5 :   CHECK(context);
    2252           5 :   const v8::HeapGraphNode* receiver = GetProperty(
    2253           5 :       env->GetIsolate(), g, v8::HeapGraphEdge::kInternal, "receiver");
    2254           5 :   CHECK(receiver);
    2255             :   const v8::HeapGraphNode* parameters_and_registers =
    2256           5 :       GetProperty(env->GetIsolate(), g, v8::HeapGraphEdge::kInternal,
    2257           5 :                   "parameters_and_registers");
    2258           5 :   CHECK(parameters_and_registers);
    2259           5 : }
    2260             : 
    2261          20 : bool HasWeakEdge(const v8::HeapGraphNode* node) {
    2262         140 :   for (int i = 0; i < node->GetChildrenCount(); ++i) {
    2263          65 :     const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
    2264          65 :     if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
    2265             :   }
    2266             :   return false;
    2267             : }
    2268             : 
    2269             : 
    2270          10 : bool HasWeakGlobalHandle() {
    2271          10 :   v8::Isolate* isolate = CcTest::isolate();
    2272          10 :   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
    2273          10 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2274          10 :   CHECK(ValidateSnapshot(snapshot));
    2275          10 :   const v8::HeapGraphNode* gc_roots = GetNode(
    2276          10 :       snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
    2277          10 :   CHECK(gc_roots);
    2278             :   const v8::HeapGraphNode* global_handles = GetNode(
    2279          10 :       gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
    2280          10 :   CHECK(global_handles);
    2281          10 :   return HasWeakEdge(global_handles);
    2282             : }
    2283             : 
    2284             : 
    2285           0 : static void PersistentHandleCallback(
    2286             :     const v8::WeakCallbackInfo<v8::Persistent<v8::Object> >& data) {
    2287             :   data.GetParameter()->Reset();
    2288           0 : }
    2289             : 
    2290             : 
    2291       26644 : TEST(WeakGlobalHandle) {
    2292           5 :   LocalContext env;
    2293          10 :   v8::HandleScope scope(env->GetIsolate());
    2294             : 
    2295           5 :   CHECK(!HasWeakGlobalHandle());
    2296             : 
    2297             :   v8::Persistent<v8::Object> handle;
    2298             : 
    2299          15 :   handle.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
    2300             :   handle.SetWeak(&handle, PersistentHandleCallback,
    2301             :                  v8::WeakCallbackType::kParameter);
    2302             : 
    2303           5 :   CHECK(HasWeakGlobalHandle());
    2304           5 :   CcTest::CollectAllGarbage();
    2305           5 :   EmptyMessageQueues(env->GetIsolate());
    2306           5 : }
    2307             : 
    2308             : 
    2309       26644 : TEST(SfiAndJsFunctionWeakRefs) {
    2310           5 :   LocalContext env;
    2311          10 :   v8::HandleScope scope(env->GetIsolate());
    2312           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2313             : 
    2314             :   CompileRun(
    2315             :       "fun = (function (x) { return function () { return x + 1; } })(1);");
    2316           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2317           5 :   CHECK(ValidateSnapshot(snapshot));
    2318           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2319           5 :   CHECK(global);
    2320           5 :   const v8::HeapGraphNode* fun = GetProperty(
    2321           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "fun");
    2322           5 :   CHECK(!HasWeakEdge(fun));
    2323           5 :   const v8::HeapGraphNode* shared = GetProperty(
    2324           5 :       env->GetIsolate(), fun, v8::HeapGraphEdge::kInternal, "shared");
    2325           5 :   CHECK(!HasWeakEdge(shared));
    2326           5 : }
    2327             : 
    2328             : 
    2329       26644 : TEST(AllStrongGcRootsHaveNames) {
    2330           5 :   LocalContext env;
    2331          10 :   v8::HandleScope scope(env->GetIsolate());
    2332           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2333             : 
    2334             :   CompileRun("foo = {};");
    2335           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2336           5 :   CHECK(ValidateSnapshot(snapshot));
    2337           5 :   const v8::HeapGraphNode* gc_roots = GetNode(
    2338           5 :       snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
    2339           5 :   CHECK(gc_roots);
    2340             :   const v8::HeapGraphNode* strong_roots = GetNode(
    2341           5 :       gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
    2342           5 :   CHECK(strong_roots);
    2343         555 :   for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
    2344         275 :     const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
    2345         275 :     CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
    2346         825 :     v8::String::Utf8Value name(env->GetIsolate(), edge->GetName());
    2347         550 :     CHECK(isalpha(**name));
    2348             :   }
    2349           5 : }
    2350             : 
    2351             : 
    2352       26644 : TEST(NoRefsToNonEssentialEntries) {
    2353           5 :   LocalContext env;
    2354          10 :   v8::HandleScope scope(env->GetIsolate());
    2355           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2356             :   CompileRun("global_object = {};\n");
    2357           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2358           5 :   CHECK(ValidateSnapshot(snapshot));
    2359           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2360           5 :   const v8::HeapGraphNode* global_object = GetProperty(
    2361           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "global_object");
    2362           5 :   CHECK(global_object);
    2363             :   const v8::HeapGraphNode* properties =
    2364           5 :       GetProperty(env->GetIsolate(), global_object,
    2365           5 :                   v8::HeapGraphEdge::kInternal, "properties");
    2366           5 :   CHECK(!properties);
    2367             :   const v8::HeapGraphNode* elements =
    2368           5 :       GetProperty(env->GetIsolate(), global_object,
    2369           5 :                   v8::HeapGraphEdge::kInternal, "elements");
    2370           5 :   CHECK(!elements);
    2371           5 : }
    2372             : 
    2373             : 
    2374       26644 : TEST(MapHasDescriptorsAndTransitions) {
    2375           5 :   LocalContext env;
    2376          10 :   v8::HandleScope scope(env->GetIsolate());
    2377           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2378             :   CompileRun("obj = { a: 10 };\n");
    2379           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2380           5 :   CHECK(ValidateSnapshot(snapshot));
    2381           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2382           5 :   const v8::HeapGraphNode* global_object = GetProperty(
    2383           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj");
    2384           5 :   CHECK(global_object);
    2385             : 
    2386           5 :   const v8::HeapGraphNode* map = GetProperty(
    2387           5 :       env->GetIsolate(), global_object, v8::HeapGraphEdge::kInternal, "map");
    2388           5 :   CHECK(map);
    2389           5 :   const v8::HeapGraphNode* own_descriptors = GetProperty(
    2390           5 :       env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "descriptors");
    2391           5 :   CHECK(own_descriptors);
    2392           5 :   const v8::HeapGraphNode* own_transitions = GetProperty(
    2393           5 :       env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "transitions");
    2394           5 :   CHECK(!own_transitions);
    2395           5 : }
    2396             : 
    2397             : 
    2398       26644 : TEST(ManyLocalsInSharedContext) {
    2399             :   // This test gets very slow with slow asserts (18 minutes instead of 1:30,
    2400             :   // as of November 2018).
    2401             : #ifdef ENABLE_SLOW_DCHECKS
    2402             :   i::FLAG_enable_slow_asserts = false;
    2403             : #endif
    2404           5 :   LocalContext env;
    2405          10 :   v8::HandleScope scope(env->GetIsolate());
    2406           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2407             :   int num_objects = 6000;
    2408             :   CompileRun(
    2409             :       "var n = 6000;"
    2410             :       "var result = [];"
    2411             :       "result.push('(function outer() {');"
    2412             :       "for (var i = 0; i < n; i++) {"
    2413             :       "    var f = 'function f_' + i + '() { ';"
    2414             :       "    if (i > 0)"
    2415             :       "        f += 'f_' + (i - 1) + '();';"
    2416             :       "    f += ' }';"
    2417             :       "    result.push(f);"
    2418             :       "}"
    2419             :       "result.push('return f_' + (n - 1) + ';');"
    2420             :       "result.push('})()');"
    2421             :       "var ok = eval(result.join('\\n'));");
    2422           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2423           5 :   CHECK(ValidateSnapshot(snapshot));
    2424             : 
    2425           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2426           5 :   CHECK(global);
    2427           5 :   const v8::HeapGraphNode* ok_object = GetProperty(
    2428           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "ok");
    2429           5 :   CHECK(ok_object);
    2430           5 :   const v8::HeapGraphNode* context_object = GetProperty(
    2431           5 :       env->GetIsolate(), ok_object, v8::HeapGraphEdge::kInternal, "context");
    2432           5 :   CHECK(context_object);
    2433             :   // Check the objects are not duplicated in the context.
    2434           5 :   CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1,
    2435             :            context_object->GetChildrenCount());
    2436             :   // Check all the objects have got their names.
    2437             :   // ... well check just every 15th because otherwise it's too slow in debug.
    2438        4005 :   for (int i = 0; i < num_objects - 1; i += 15) {
    2439             :     i::EmbeddedVector<char, 100> var_name;
    2440        2000 :     i::SNPrintF(var_name, "f_%d", i);
    2441             :     const v8::HeapGraphNode* f_object =
    2442        2000 :         GetProperty(env->GetIsolate(), context_object,
    2443        2000 :                     v8::HeapGraphEdge::kContextVariable, var_name.start());
    2444        2000 :     CHECK(f_object);
    2445             :   }
    2446           5 : }
    2447             : 
    2448             : 
    2449       26639 : TEST(AllocationSitesAreVisible) {
    2450             :   if (i::FLAG_lite_mode) return;
    2451             : 
    2452           0 :   LocalContext env;
    2453           0 :   v8::Isolate* isolate = env->GetIsolate();
    2454           0 :   v8::HandleScope scope(isolate);
    2455           0 :   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
    2456             :   CompileRun(
    2457             :       "fun = function () { var a = [3, 2, 1]; return a; }\n"
    2458             :       "fun();");
    2459           0 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2460           0 :   CHECK(ValidateSnapshot(snapshot));
    2461             : 
    2462           0 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2463           0 :   CHECK(global);
    2464           0 :   const v8::HeapGraphNode* fun_code = GetProperty(
    2465           0 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "fun");
    2466           0 :   CHECK(fun_code);
    2467             :   const v8::HeapGraphNode* feedback_cell =
    2468           0 :       GetProperty(env->GetIsolate(), fun_code, v8::HeapGraphEdge::kInternal,
    2469           0 :                   "feedback_cell");
    2470           0 :   CHECK(feedback_cell);
    2471           0 :   const v8::HeapGraphNode* vector = GetProperty(
    2472           0 :       env->GetIsolate(), feedback_cell, v8::HeapGraphEdge::kInternal, "value");
    2473           0 :   CHECK_EQ(v8::HeapGraphNode::kArray, vector->GetType());
    2474           0 :   CHECK_EQ(3, vector->GetChildrenCount());
    2475             : 
    2476             :   // The first value in the feedback vector should be the boilerplate,
    2477             :   // after an AllocationSite.
    2478           0 :   const v8::HeapGraphEdge* prop = vector->GetChild(2);
    2479           0 :   const v8::HeapGraphNode* allocation_site = prop->GetToNode();
    2480           0 :   v8::String::Utf8Value name(env->GetIsolate(), allocation_site->GetName());
    2481           0 :   CHECK_EQ(0, strcmp("system / AllocationSite", *name));
    2482             :   const v8::HeapGraphNode* transition_info =
    2483           0 :       GetProperty(env->GetIsolate(), allocation_site,
    2484           0 :                   v8::HeapGraphEdge::kInternal, "transition_info");
    2485           0 :   CHECK(transition_info);
    2486             : 
    2487             :   const v8::HeapGraphNode* elements =
    2488           0 :       GetProperty(env->GetIsolate(), transition_info,
    2489           0 :                   v8::HeapGraphEdge::kInternal, "elements");
    2490           0 :   CHECK(elements);
    2491           0 :   CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType());
    2492           0 :   CHECK_EQ(v8::internal::FixedArray::SizeFor(3),
    2493             :            static_cast<int>(elements->GetShallowSize()));
    2494             : 
    2495             :   v8::Local<v8::Value> array_val =
    2496           0 :       heap_profiler->FindObjectById(transition_info->GetId());
    2497           0 :   CHECK(array_val->IsArray());
    2498             :   v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(array_val);
    2499             :   // Verify the array is "a" in the code above.
    2500           0 :   CHECK_EQ(3u, array->Length());
    2501           0 :   CHECK(v8::Integer::New(isolate, 3)
    2502             :             ->Equals(env.local(),
    2503             :                      array->Get(env.local(), v8::Integer::New(isolate, 0))
    2504             :                          .ToLocalChecked())
    2505             :             .FromJust());
    2506           0 :   CHECK(v8::Integer::New(isolate, 2)
    2507             :             ->Equals(env.local(),
    2508             :                      array->Get(env.local(), v8::Integer::New(isolate, 1))
    2509             :                          .ToLocalChecked())
    2510             :             .FromJust());
    2511           0 :   CHECK(v8::Integer::New(isolate, 1)
    2512             :             ->Equals(env.local(),
    2513             :                      array->Get(env.local(), v8::Integer::New(isolate, 2))
    2514             :                          .ToLocalChecked())
    2515             :             .FromJust());
    2516             : }
    2517             : 
    2518             : 
    2519       26644 : TEST(JSFunctionHasCodeLink) {
    2520           5 :   LocalContext env;
    2521          10 :   v8::HandleScope scope(env->GetIsolate());
    2522           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2523             :   CompileRun("function foo(x, y) { return x + y; }\n");
    2524           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2525           5 :   CHECK(ValidateSnapshot(snapshot));
    2526           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2527           5 :   const v8::HeapGraphNode* foo_func = GetProperty(
    2528           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "foo");
    2529           5 :   CHECK(foo_func);
    2530           5 :   const v8::HeapGraphNode* code = GetProperty(
    2531           5 :       env->GetIsolate(), foo_func, v8::HeapGraphEdge::kInternal, "code");
    2532           5 :   CHECK(code);
    2533           5 : }
    2534             : 
    2535          10 : static const v8::HeapGraphNode* GetNodeByPath(v8::Isolate* isolate,
    2536             :                                               const v8::HeapSnapshot* snapshot,
    2537             :                                               const char* path[], int depth) {
    2538          10 :   const v8::HeapGraphNode* node = snapshot->GetRoot();
    2539          70 :   for (int current_depth = 0; current_depth < depth; ++current_depth) {
    2540          30 :     int i, count = node->GetChildrenCount();
    2541        2100 :     for (i = 0; i < count; ++i) {
    2542        1065 :       const v8::HeapGraphEdge* edge = node->GetChild(i);
    2543        1065 :       const v8::HeapGraphNode* to_node = edge->GetToNode();
    2544        2100 :       v8::String::Utf8Value edge_name(isolate, edge->GetName());
    2545        3165 :       v8::String::Utf8Value node_name(isolate, to_node->GetName());
    2546             :       i::EmbeddedVector<char, 100> name;
    2547        1065 :       i::SNPrintF(name, "%s::%s", *edge_name, *node_name);
    2548        2130 :       if (strstr(name.start(), path[current_depth])) {
    2549             :         node = to_node;
    2550          30 :         break;
    2551             :       }
    2552             :     }
    2553          30 :     if (i == count) return nullptr;
    2554             :   }
    2555             :   return node;
    2556             : }
    2557             : 
    2558             : 
    2559       26644 : TEST(CheckCodeNames) {
    2560           5 :   LocalContext env;
    2561          10 :   v8::HandleScope scope(env->GetIsolate());
    2562           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2563             :   CompileRun("var a = 1.1;");
    2564           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2565           5 :   CHECK(ValidateSnapshot(snapshot));
    2566             : 
    2567             :   const char* builtin_path1[] = {"::(GC roots)", "::(Builtins)",
    2568           5 :                                  "::(KeyedLoadIC_Slow builtin)"};
    2569           5 :   const v8::HeapGraphNode* node = GetNodeByPath(
    2570           5 :       env->GetIsolate(), snapshot, builtin_path1, arraysize(builtin_path1));
    2571           5 :   CHECK(node);
    2572             : 
    2573             :   const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)",
    2574           5 :                                  "::(CompileLazy builtin)"};
    2575           5 :   node = GetNodeByPath(env->GetIsolate(), snapshot, builtin_path2,
    2576           5 :                        arraysize(builtin_path2));
    2577           5 :   CHECK(node);
    2578          15 :   v8::String::Utf8Value node_name(env->GetIsolate(), node->GetName());
    2579           5 :   CHECK_EQ(0, strcmp("(CompileLazy builtin)", *node_name));
    2580           5 : }
    2581             : 
    2582             : 
    2583             : static const char* record_trace_tree_source =
    2584             : "var topFunctions = [];\n"
    2585             : "var global = this;\n"
    2586             : "function generateFunctions(width, depth) {\n"
    2587             : "  var script = [];\n"
    2588             : "  for (var i = 0; i < width; i++) {\n"
    2589             : "    for (var j = 0; j < depth; j++) {\n"
    2590             : "      script.push('function f_' + i + '_' + j + '(x) {\\n');\n"
    2591             : "      script.push('  try {\\n');\n"
    2592             : "      if (j < depth-2) {\n"
    2593             : "        script.push('    return f_' + i + '_' + (j+1) + '(x+1);\\n');\n"
    2594             : "      } else if (j == depth - 2) {\n"
    2595             : "        script.push('    return new f_' + i + '_' + (depth - 1) + '();\\n');\n"
    2596             : "      } else if (j == depth - 1) {\n"
    2597             : "        script.push('    this.ts = Date.now();\\n');\n"
    2598             : "      }\n"
    2599             : "      script.push('  } catch (e) {}\\n');\n"
    2600             : "      script.push('}\\n');\n"
    2601             : "      \n"
    2602             : "    }\n"
    2603             : "  }\n"
    2604             : "  var script = script.join('');\n"
    2605             : "  // throw script;\n"
    2606             : "  global.eval(script);\n"
    2607             : "  for (var i = 0; i < width; i++) {\n"
    2608             : "    topFunctions.push(this['f_' + i + '_0']);\n"
    2609             : "  }\n"
    2610             : "}\n"
    2611             : "\n"
    2612             : "var width = 3;\n"
    2613             : "var depth = 3;\n"
    2614             : "generateFunctions(width, depth);\n"
    2615             : "var instances = [];\n"
    2616             : "function start() {\n"
    2617             : "  for (var i = 0; i < width; i++) {\n"
    2618             : "    instances.push(topFunctions[i](0));\n"
    2619             : "  }\n"
    2620             : "}\n"
    2621             : "\n"
    2622             : "for (var i = 0; i < 100; i++) start();\n";
    2623             : 
    2624             : 
    2625          30 : static AllocationTraceNode* FindNode(
    2626             :     AllocationTracker* tracker, const Vector<const char*>& names) {
    2627             :   AllocationTraceNode* node = tracker->trace_tree()->root();
    2628         330 :   for (int i = 0; node != nullptr && i < names.length(); i++) {
    2629         180 :     const char* name = names[i];
    2630             :     const std::vector<AllocationTraceNode*>& children = node->children();
    2631             :     node = nullptr;
    2632         125 :     for (AllocationTraceNode* child : children) {
    2633             :       unsigned index = child->function_info_index();
    2634             :       AllocationTracker::FunctionInfo* info =
    2635         250 :           tracker->function_info_list()[index];
    2636         125 :       if (info && strcmp(info->name, name) == 0) {
    2637             :         node = child;
    2638             :         break;
    2639             :       }
    2640             :     }
    2641             :   }
    2642          30 :   return node;
    2643             : }
    2644             : 
    2645             : 
    2646       26644 : TEST(ArrayGrowLeftTrim) {
    2647           5 :   LocalContext env;
    2648          10 :   v8::HandleScope scope(env->GetIsolate());
    2649           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2650           5 :   heap_profiler->StartTrackingHeapObjects(true);
    2651             : 
    2652             :   CompileRun(
    2653             :     "var a = [];\n"
    2654             :     "for (var i = 0; i < 5; ++i)\n"
    2655             :     "    a[i] = i;\n"
    2656             :     "for (var i = 0; i < 3; ++i)\n"
    2657             :     "    a.shift();\n");
    2658             : 
    2659           5 :   const char* names[] = {""};
    2660             :   AllocationTracker* tracker =
    2661             :       reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
    2662           5 :   CHECK(tracker);
    2663             :   // Resolve all function locations.
    2664           5 :   tracker->PrepareForSerialization();
    2665             :   // Print for better diagnostics in case of failure.
    2666           5 :   tracker->trace_tree()->Print(tracker);
    2667             : 
    2668           5 :   AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
    2669           5 :   CHECK(node);
    2670           5 :   CHECK_GE(node->allocation_count(), 2u);
    2671           5 :   CHECK_GE(node->allocation_size(), 4u * 5u);
    2672           5 :   heap_profiler->StopTrackingHeapObjects();
    2673           5 : }
    2674             : 
    2675       26644 : TEST(TrackHeapAllocationsWithInlining) {
    2676          10 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    2677           5 :   LocalContext env;
    2678             : 
    2679           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2680           5 :   heap_profiler->StartTrackingHeapObjects(true);
    2681             : 
    2682           5 :   CompileRun(record_trace_tree_source);
    2683             : 
    2684             :   AllocationTracker* tracker =
    2685             :       reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
    2686           5 :   CHECK(tracker);
    2687             :   // Resolve all function locations.
    2688           5 :   tracker->PrepareForSerialization();
    2689             :   // Print for better diagnostics in case of failure.
    2690           5 :   tracker->trace_tree()->Print(tracker);
    2691             : 
    2692           5 :   const char* names[] = {"", "start", "f_0_0"};
    2693           5 :   AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
    2694           5 :   CHECK(node);
    2695             :   // In lite mode, there is feedback and feedback metadata.
    2696             :   unsigned int num_nodes = (i::FLAG_lite_mode) ? 6 : 8;
    2697             :   // Without forced source position collection, there is no source position
    2698             :   // table.
    2699           5 :   if (i::FLAG_enable_lazy_source_positions) num_nodes -= 1;
    2700           5 :   CHECK_GE(node->allocation_count(), num_nodes);
    2701           5 :   CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
    2702           5 :   heap_profiler->StopTrackingHeapObjects();
    2703           5 : }
    2704             : 
    2705       26644 : TEST(TrackHeapAllocationsWithoutInlining) {
    2706           5 :   i::FLAG_turbo_inlining = false;
    2707             :   // Disable inlining
    2708           5 :   i::FLAG_max_inlined_bytecode_size = 0;
    2709           5 :   i::FLAG_max_inlined_bytecode_size_small = 0;
    2710          10 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    2711           5 :   LocalContext env;
    2712             : 
    2713           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2714           5 :   heap_profiler->StartTrackingHeapObjects(true);
    2715             : 
    2716           5 :   CompileRun(record_trace_tree_source);
    2717             : 
    2718             :   AllocationTracker* tracker =
    2719             :       reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
    2720           5 :   CHECK(tracker);
    2721             :   // Resolve all function locations.
    2722           5 :   tracker->PrepareForSerialization();
    2723             :   // Print for better diagnostics in case of failure.
    2724           5 :   tracker->trace_tree()->Print(tracker);
    2725             : 
    2726           5 :   const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
    2727           5 :   AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
    2728           5 :   CHECK(node);
    2729           5 :   CHECK_GE(node->allocation_count(), 100u);
    2730           5 :   CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
    2731           5 :   heap_profiler->StopTrackingHeapObjects();
    2732           5 : }
    2733             : 
    2734             : 
    2735             : static const char* inline_heap_allocation_source =
    2736             :     "function f_0(x) {\n"
    2737             :     "  return f_1(x+1);\n"
    2738             :     "}\n"
    2739             :     "%NeverOptimizeFunction(f_0);\n"
    2740             :     "function f_1(x) {\n"
    2741             :     "  return new f_2(x+1);\n"
    2742             :     "}\n"
    2743             :     "%NeverOptimizeFunction(f_1);\n"
    2744             :     "function f_2(x) {\n"
    2745             :     "  this.foo = x;\n"
    2746             :     "}\n"
    2747             :     "var instances = [];\n"
    2748             :     "function start() {\n"
    2749             :     "  instances.push(f_0(0));\n"
    2750             :     "}\n"
    2751             :     "\n"
    2752             :     "for (var i = 0; i < 100; i++) start();\n";
    2753             : 
    2754             : 
    2755       26644 : TEST(TrackBumpPointerAllocations) {
    2756           5 :   i::FLAG_allow_natives_syntax = true;
    2757          10 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    2758           5 :   LocalContext env;
    2759             : 
    2760           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2761           5 :   const char* names[] = {"", "start", "f_0", "f_1"};
    2762             :   // First check that normally all allocations are recorded.
    2763             :   {
    2764           5 :     heap_profiler->StartTrackingHeapObjects(true);
    2765             : 
    2766           5 :     CompileRun(inline_heap_allocation_source);
    2767             : 
    2768             :     AllocationTracker* tracker =
    2769             :         reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
    2770           5 :     CHECK(tracker);
    2771             :     // Resolve all function locations.
    2772           5 :     tracker->PrepareForSerialization();
    2773             :     // Print for better diagnostics in case of failure.
    2774           5 :     tracker->trace_tree()->Print(tracker);
    2775             : 
    2776           5 :     AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
    2777           5 :     CHECK(node);
    2778           5 :     CHECK_GE(node->allocation_count(), 100u);
    2779           5 :     CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
    2780           5 :     heap_profiler->StopTrackingHeapObjects();
    2781             :   }
    2782             : 
    2783             :   {
    2784           5 :     heap_profiler->StartTrackingHeapObjects(true);
    2785             : 
    2786             :     // Now check that not all allocations are tracked if we manually reenable
    2787             :     // inline allocations.
    2788           5 :     CHECK(CcTest::heap()->inline_allocation_disabled());
    2789           5 :     CcTest::heap()->EnableInlineAllocation();
    2790             : 
    2791           5 :     CompileRun(inline_heap_allocation_source);
    2792             : 
    2793             :     AllocationTracker* tracker =
    2794             :         reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
    2795           5 :     CHECK(tracker);
    2796             :     // Resolve all function locations.
    2797           5 :     tracker->PrepareForSerialization();
    2798             :     // Print for better diagnostics in case of failure.
    2799           5 :     tracker->trace_tree()->Print(tracker);
    2800             : 
    2801           5 :     AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
    2802           5 :     CHECK(node);
    2803           5 :     CHECK_LT(node->allocation_count(), 100u);
    2804             : 
    2805           5 :     CcTest::heap()->DisableInlineAllocation();
    2806           5 :     heap_profiler->StopTrackingHeapObjects();
    2807             :   }
    2808           5 : }
    2809             : 
    2810             : 
    2811       26644 : TEST(TrackV8ApiAllocation) {
    2812          10 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    2813           5 :   LocalContext env;
    2814             : 
    2815           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2816           5 :   const char* names[] = { "(V8 API)" };
    2817           5 :   heap_profiler->StartTrackingHeapObjects(true);
    2818             : 
    2819           5 :   v8::Local<v8::Object> o1 = v8::Object::New(env->GetIsolate());
    2820           5 :   o1->Clone();
    2821             : 
    2822             :   AllocationTracker* tracker =
    2823             :       reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
    2824           5 :   CHECK(tracker);
    2825             :   // Resolve all function locations.
    2826           5 :   tracker->PrepareForSerialization();
    2827             :   // Print for better diagnostics in case of failure.
    2828           5 :   tracker->trace_tree()->Print(tracker);
    2829             : 
    2830           5 :   AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
    2831           5 :   CHECK(node);
    2832           5 :   CHECK_GE(node->allocation_count(), 2u);
    2833           5 :   CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
    2834           5 :   heap_profiler->StopTrackingHeapObjects();
    2835           5 : }
    2836             : 
    2837             : 
    2838       26644 : TEST(ArrayBufferAndArrayBufferView) {
    2839           5 :   LocalContext env;
    2840          10 :   v8::HandleScope scope(env->GetIsolate());
    2841           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2842             :   CompileRun("arr1 = new Uint32Array(100);\n");
    2843           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2844           5 :   CHECK(ValidateSnapshot(snapshot));
    2845           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2846           5 :   const v8::HeapGraphNode* arr1_obj = GetProperty(
    2847           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "arr1");
    2848           5 :   CHECK(arr1_obj);
    2849           5 :   const v8::HeapGraphNode* arr1_buffer = GetProperty(
    2850           5 :       env->GetIsolate(), arr1_obj, v8::HeapGraphEdge::kInternal, "buffer");
    2851           5 :   CHECK(arr1_buffer);
    2852             :   const v8::HeapGraphNode* backing_store =
    2853           5 :       GetProperty(env->GetIsolate(), arr1_buffer, v8::HeapGraphEdge::kInternal,
    2854           5 :                   "backing_store");
    2855           5 :   CHECK(backing_store);
    2856           5 :   CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize()));
    2857           5 : }
    2858             : 
    2859             : 
    2860           5 : static int GetRetainersCount(const v8::HeapSnapshot* snapshot,
    2861             :                              const v8::HeapGraphNode* node) {
    2862             :   int count = 0;
    2863       33799 :   for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) {
    2864       33794 :     const v8::HeapGraphNode* parent = snapshot->GetNode(i);
    2865      154706 :     for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) {
    2866      120912 :       if (parent->GetChild(j)->GetToNode() == node) {
    2867          10 :         ++count;
    2868             :       }
    2869             :     }
    2870             :   }
    2871           5 :   return count;
    2872             : }
    2873             : 
    2874             : 
    2875       26644 : TEST(ArrayBufferSharedBackingStore) {
    2876           5 :   LocalContext env;
    2877           5 :   v8::Isolate* isolate = env->GetIsolate();
    2878          10 :   v8::HandleScope handle_scope(isolate);
    2879           5 :   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
    2880             : 
    2881           5 :   v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
    2882           5 :   CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
    2883           5 :   CHECK(!ab->IsExternal());
    2884           5 :   v8::ArrayBuffer::Contents ab_contents = ab->Externalize();
    2885           5 :   CHECK(ab->IsExternal());
    2886             : 
    2887           5 :   CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
    2888             :   void* data = ab_contents.Data();
    2889           5 :   CHECK_NOT_NULL(data);
    2890             :   v8::Local<v8::ArrayBuffer> ab2 =
    2891           5 :       v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength());
    2892           5 :   CHECK(ab2->IsExternal());
    2893          20 :   env->Global()->Set(env.local(), v8_str("ab1"), ab).FromJust();
    2894          20 :   env->Global()->Set(env.local(), v8_str("ab2"), ab2).FromJust();
    2895             : 
    2896             :   v8::Local<v8::Value> result = CompileRun("ab2.byteLength");
    2897          10 :   CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
    2898             : 
    2899           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2900           5 :   CHECK(ValidateSnapshot(snapshot));
    2901           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2902           5 :   const v8::HeapGraphNode* ab1_node = GetProperty(
    2903           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "ab1");
    2904           5 :   CHECK(ab1_node);
    2905             :   const v8::HeapGraphNode* ab1_data =
    2906           5 :       GetProperty(env->GetIsolate(), ab1_node, v8::HeapGraphEdge::kInternal,
    2907           5 :                   "backing_store");
    2908           5 :   CHECK(ab1_data);
    2909           5 :   const v8::HeapGraphNode* ab2_node = GetProperty(
    2910           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "ab2");
    2911           5 :   CHECK(ab2_node);
    2912             :   const v8::HeapGraphNode* ab2_data =
    2913           5 :       GetProperty(env->GetIsolate(), ab2_node, v8::HeapGraphEdge::kInternal,
    2914           5 :                   "backing_store");
    2915           5 :   CHECK(ab2_data);
    2916           5 :   CHECK_EQ(ab1_data, ab2_data);
    2917           5 :   CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data));
    2918           5 :   free(data);
    2919           5 : }
    2920             : 
    2921             : 
    2922       26644 : TEST(WeakContainers) {
    2923           5 :   i::FLAG_allow_natives_syntax = true;
    2924           3 :   LocalContext env;
    2925           8 :   v8::HandleScope scope(env->GetIsolate());
    2926           7 :   if (!CcTest::i_isolate()->use_optimizer()) return;
    2927           4 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2928             :   CompileRun(
    2929             :       "function foo(a) { return a.x; }\n"
    2930             :       "obj = {x : 123};\n"
    2931             :       "foo(obj);\n"
    2932             :       "foo(obj);\n"
    2933             :       "%OptimizeFunctionOnNextCall(foo);\n"
    2934             :       "foo(obj);\n");
    2935           4 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2936           4 :   CHECK(ValidateSnapshot(snapshot));
    2937           4 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2938           4 :   const v8::HeapGraphNode* obj = GetProperty(
    2939           4 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj");
    2940           4 :   CHECK(obj);
    2941             :   const v8::HeapGraphNode* map =
    2942           4 :       GetProperty(env->GetIsolate(), obj, v8::HeapGraphEdge::kInternal, "map");
    2943           4 :   CHECK(map);
    2944           4 :   const v8::HeapGraphNode* dependent_code = GetProperty(
    2945           4 :       env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "dependent_code");
    2946           4 :   if (!dependent_code) return;
    2947           3 :   int count = dependent_code->GetChildrenCount();
    2948           3 :   CHECK_NE(0, count);
    2949          15 :   for (int i = 0; i < count; ++i) {
    2950           6 :     const v8::HeapGraphEdge* prop = dependent_code->GetChild(i);
    2951           6 :     CHECK(prop->GetType() == v8::HeapGraphEdge::kInternal ||
    2952             :           prop->GetType() == v8::HeapGraphEdge::kWeak);
    2953             :   }
    2954             : }
    2955             : 
    2956       26644 : TEST(JSPromise) {
    2957           5 :   LocalContext env;
    2958          10 :   v8::HandleScope scope(env->GetIsolate());
    2959           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2960             :   CompileRun(
    2961             :       "function A() {}\n"
    2962             :       "function B() {}\n"
    2963             :       "resolved = Promise.resolve(new A());\n"
    2964             :       "rejected = Promise.reject(new B());\n"
    2965             :       "pending = new Promise(() => 0);\n"
    2966             :       "chained = pending.then(A, B);\n");
    2967           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    2968           5 :   CHECK(ValidateSnapshot(snapshot));
    2969           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    2970             : 
    2971           5 :   const v8::HeapGraphNode* resolved = GetProperty(
    2972           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "resolved");
    2973           5 :   CHECK(GetProperty(env->GetIsolate(), resolved, v8::HeapGraphEdge::kInternal,
    2974             :                     "reactions_or_result"));
    2975             : 
    2976           5 :   const v8::HeapGraphNode* rejected = GetProperty(
    2977           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "rejected");
    2978           5 :   CHECK(GetProperty(env->GetIsolate(), rejected, v8::HeapGraphEdge::kInternal,
    2979             :                     "reactions_or_result"));
    2980             : 
    2981           5 :   const v8::HeapGraphNode* pending = GetProperty(
    2982           5 :       env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "pending");
    2983           5 :   CHECK(GetProperty(env->GetIsolate(), pending, v8::HeapGraphEdge::kInternal,
    2984             :                     "reactions_or_result"));
    2985             : 
    2986           5 :   const char* objectNames[] = {"resolved", "rejected", "pending", "chained"};
    2987          45 :   for (auto objectName : objectNames) {
    2988          20 :     const v8::HeapGraphNode* promise = GetProperty(
    2989          20 :         env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, objectName);
    2990          20 :     EnsureNoUninstrumentedInternals(env->GetIsolate(), promise);
    2991             :   }
    2992           5 : }
    2993             : 
    2994       26644 : TEST(HeapSnapshotScriptContext) {
    2995           5 :   LocalContext env;
    2996          10 :   v8::HandleScope scope(env->GetIsolate());
    2997           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    2998             : 
    2999             :   CompileRun("class Foo{}; const foo = new Foo();");
    3000           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    3001           5 :   CHECK(ValidateSnapshot(snapshot));
    3002           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    3003             :   const v8::HeapGraphNode* native_context =
    3004           5 :       GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kInternal,
    3005           5 :                   "native_context");
    3006           5 :   CHECK(native_context);
    3007             :   const v8::HeapGraphNode* script_context_table =
    3008           5 :       GetProperty(env->GetIsolate(), native_context,
    3009           5 :                   v8::HeapGraphEdge::kInternal, "script_context_table");
    3010           5 :   CHECK(script_context_table);
    3011             :   bool found_foo = false;
    3012          20 :   for (int i = 0, count = script_context_table->GetChildrenCount(); i < count;
    3013             :        ++i) {
    3014             :     const v8::HeapGraphNode* context =
    3015          15 :         script_context_table->GetChild(i)->GetToNode();
    3016          15 :     const v8::HeapGraphNode* foo = GetProperty(
    3017          15 :         env->GetIsolate(), context, v8::HeapGraphEdge::kContextVariable, "foo");
    3018          15 :     if (foo) {
    3019             :       found_foo = true;
    3020             :     }
    3021             :   }
    3022           5 :   CHECK(found_foo);
    3023           5 : }
    3024             : 
    3025         150 : class EmbedderNode : public v8::EmbedderGraph::Node {
    3026             :  public:
    3027             :   EmbedderNode(const char* name, size_t size,
    3028             :                v8::EmbedderGraph::Node* wrapper_node = nullptr)
    3029          75 :       : name_(name), size_(size), wrapper_node_(wrapper_node) {}
    3030             : 
    3031             :   // Graph::Node overrides.
    3032          75 :   const char* Name() override { return name_; }
    3033          65 :   size_t SizeInBytes() override { return size_; }
    3034         200 :   Node* WrapperNode() override { return wrapper_node_; }
    3035             : 
    3036             :  private:
    3037             :   const char* name_;
    3038             :   size_t size_;
    3039             :   Node* wrapper_node_;
    3040             : };
    3041             : 
    3042          10 : class EmbedderRootNode : public EmbedderNode {
    3043             :  public:
    3044           5 :   explicit EmbedderRootNode(const char* name) : EmbedderNode(name, 0) {}
    3045             :   // Graph::Node override.
    3046          10 :   bool IsRootNode() override { return true; }
    3047             : };
    3048             : 
    3049             : // Used to pass the global object to the BuildEmbedderGraph callback.
    3050             : // Otherwise, the callback has to iterate the global handles to find the
    3051             : // global object.
    3052             : v8::Local<v8::Value>* global_object_pointer;
    3053             : 
    3054           5 : void BuildEmbedderGraph(v8::Isolate* v8_isolate, v8::EmbedderGraph* graph,
    3055             :                         void* data) {
    3056             :   using Node = v8::EmbedderGraph::Node;
    3057           5 :   Node* global_node = graph->V8Node(*global_object_pointer);
    3058          10 :   Node* embedder_node_A = graph->AddNode(
    3059          10 :       std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeA", 10)));
    3060          10 :   Node* embedder_node_B = graph->AddNode(
    3061          10 :       std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeB", 20)));
    3062          10 :   Node* embedder_node_C = graph->AddNode(
    3063          10 :       std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeC", 30)));
    3064          10 :   Node* embedder_root = graph->AddNode(
    3065          10 :       std::unique_ptr<Node>(new EmbedderRootNode("EmbedderRoot")));
    3066           5 :   graph->AddEdge(global_node, embedder_node_A);
    3067           5 :   graph->AddEdge(embedder_node_A, embedder_node_B);
    3068           5 :   graph->AddEdge(embedder_root, embedder_node_C);
    3069           5 :   graph->AddEdge(embedder_node_C, global_node);
    3070           5 : }
    3071             : 
    3072           5 : void CheckEmbedderGraphSnapshot(v8::Isolate* isolate,
    3073             :                                 const v8::HeapSnapshot* snapshot) {
    3074           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    3075             :   const v8::HeapGraphNode* embedder_node_A =
    3076           5 :       GetChildByName(global, "EmbedderNodeA");
    3077           5 :   CHECK_EQ(10, GetSize(embedder_node_A));
    3078             :   const v8::HeapGraphNode* embedder_node_B =
    3079           5 :       GetChildByName(embedder_node_A, "EmbedderNodeB");
    3080           5 :   CHECK_EQ(20, GetSize(embedder_node_B));
    3081             :   const v8::HeapGraphNode* embedder_root =
    3082             :       GetRootChild(snapshot, "EmbedderRoot");
    3083           5 :   CHECK(embedder_root);
    3084             :   const v8::HeapGraphNode* embedder_node_C =
    3085           5 :       GetChildByName(embedder_root, "EmbedderNodeC");
    3086           5 :   CHECK_EQ(30, GetSize(embedder_node_C));
    3087             :   const v8::HeapGraphNode* global_reference =
    3088           5 :       GetChildByName(embedder_node_C, "Object");
    3089           5 :   CHECK(global_reference);
    3090           5 : }
    3091             : 
    3092       26644 : TEST(EmbedderGraph) {
    3093           5 :   i::FLAG_heap_profiler_use_embedder_graph = true;
    3094           5 :   LocalContext env;
    3095          10 :   v8::HandleScope scope(env->GetIsolate());
    3096           5 :   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
    3097             :   v8::Local<v8::Value> global_object =
    3098             :       v8::Utils::ToLocal(i::Handle<i::JSObject>(
    3099          10 :           (isolate->context()->native_context()->global_object()), isolate));
    3100           5 :   global_object_pointer = &global_object;
    3101           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3102           5 :   heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraph, nullptr);
    3103           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    3104           5 :   CHECK(ValidateSnapshot(snapshot));
    3105           5 :   CheckEmbedderGraphSnapshot(env->GetIsolate(), snapshot);
    3106           5 : }
    3107             : 
    3108           5 : void BuildEmbedderGraphWithNamedEdges(v8::Isolate* v8_isolate,
    3109             :                                       v8::EmbedderGraph* graph, void* data) {
    3110             :   using Node = v8::EmbedderGraph::Node;
    3111           5 :   Node* global_node = graph->V8Node(*global_object_pointer);
    3112          10 :   Node* embedder_node_A = graph->AddNode(
    3113          10 :       std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeA", 10)));
    3114          10 :   Node* embedder_node_B = graph->AddNode(
    3115          10 :       std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeB", 20)));
    3116          10 :   Node* embedder_node_C = graph->AddNode(
    3117          10 :       std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeC", 30)));
    3118           5 :   graph->AddEdge(global_node, embedder_node_A, "global_to_a");
    3119           5 :   graph->AddEdge(embedder_node_A, embedder_node_B, "a_to_b");
    3120           5 :   graph->AddEdge(embedder_node_B, embedder_node_C);
    3121           5 : }
    3122             : 
    3123           5 : void CheckEmbedderGraphWithNamedEdges(v8::Isolate* isolate,
    3124             :                                       const v8::HeapSnapshot* snapshot) {
    3125           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    3126             :   const v8::HeapGraphEdge* global_to_a =
    3127           5 :       GetEdgeByChildName(global, "EmbedderNodeA");
    3128           5 :   CHECK(global_to_a);
    3129           5 :   CHECK_EQ(v8::HeapGraphEdge::kInternal, global_to_a->GetType());
    3130          10 :   CHECK(global_to_a->GetName()->IsString());
    3131           5 :   CHECK_EQ(0, strcmp("global_to_a", GetName(global_to_a)));
    3132           5 :   const v8::HeapGraphNode* embedder_node_A = global_to_a->GetToNode();
    3133           5 :   CHECK_EQ(0, strcmp("EmbedderNodeA", GetName(embedder_node_A)));
    3134           5 :   CHECK_EQ(10, GetSize(embedder_node_A));
    3135             : 
    3136             :   const v8::HeapGraphEdge* a_to_b =
    3137           5 :       GetEdgeByChildName(embedder_node_A, "EmbedderNodeB");
    3138           5 :   CHECK(a_to_b);
    3139          10 :   CHECK(a_to_b->GetName()->IsString());
    3140           5 :   CHECK_EQ(0, strcmp("a_to_b", GetName(a_to_b)));
    3141           5 :   CHECK_EQ(v8::HeapGraphEdge::kInternal, a_to_b->GetType());
    3142           5 :   const v8::HeapGraphNode* embedder_node_B = a_to_b->GetToNode();
    3143           5 :   CHECK_EQ(0, strcmp("EmbedderNodeB", GetName(embedder_node_B)));
    3144           5 :   CHECK_EQ(20, GetSize(embedder_node_B));
    3145             : 
    3146             :   const v8::HeapGraphEdge* b_to_c =
    3147           5 :       GetEdgeByChildName(embedder_node_B, "EmbedderNodeC");
    3148           5 :   CHECK(b_to_c);
    3149          10 :   CHECK(b_to_c->GetName()->IsNumber());
    3150           5 :   CHECK_EQ(v8::HeapGraphEdge::kElement, b_to_c->GetType());
    3151           5 :   const v8::HeapGraphNode* embedder_node_C = b_to_c->GetToNode();
    3152           5 :   CHECK_EQ(0, strcmp("EmbedderNodeC", GetName(embedder_node_C)));
    3153           5 :   CHECK_EQ(30, GetSize(embedder_node_C));
    3154           5 : }
    3155             : 
    3156       26644 : TEST(EmbedderGraphWithNamedEdges) {
    3157           5 :   i::FLAG_heap_profiler_use_embedder_graph = true;
    3158           5 :   LocalContext env;
    3159          10 :   v8::HandleScope scope(env->GetIsolate());
    3160           5 :   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
    3161             :   v8::Local<v8::Value> global_object =
    3162             :       v8::Utils::ToLocal(i::Handle<i::JSObject>(
    3163          10 :           (isolate->context()->native_context()->global_object()), isolate));
    3164           5 :   global_object_pointer = &global_object;
    3165           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3166             :   heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithNamedEdges,
    3167           5 :                                                nullptr);
    3168           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    3169           5 :   CHECK(ValidateSnapshot(snapshot));
    3170           5 :   CheckEmbedderGraphWithNamedEdges(env->GetIsolate(), snapshot);
    3171           5 : }
    3172             : 
    3173             : struct GraphBuildingContext {
    3174             :   int counter = 0;
    3175             : };
    3176             : 
    3177          10 : void CheckEmbedderGraphSnapshotWithContext(
    3178             :     v8::Isolate* isolate, const v8::HeapSnapshot* snapshot,
    3179             :     const GraphBuildingContext* context) {
    3180          10 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    3181          10 :   CHECK_GE(context->counter, 1);
    3182          10 :   CHECK_LE(context->counter, 2);
    3183             : 
    3184             :   const v8::HeapGraphNode* embedder_node_A =
    3185          10 :       GetChildByName(global, "EmbedderNodeA");
    3186          10 :   CHECK_EQ(10, GetSize(embedder_node_A));
    3187             : 
    3188             :   const v8::HeapGraphNode* embedder_node_B =
    3189          10 :       GetChildByName(global, "EmbedderNodeB");
    3190          10 :   if (context->counter == 2) {
    3191           5 :     CHECK_NOT_NULL(embedder_node_B);
    3192           5 :     CHECK_EQ(20, GetSize(embedder_node_B));
    3193             :   } else {
    3194           5 :     CHECK_NULL(embedder_node_B);
    3195             :   }
    3196          10 : }
    3197             : 
    3198          15 : void BuildEmbedderGraphWithContext(v8::Isolate* v8_isolate,
    3199             :                                    v8::EmbedderGraph* graph, void* data) {
    3200             :   using Node = v8::EmbedderGraph::Node;
    3201             :   GraphBuildingContext* context = static_cast<GraphBuildingContext*>(data);
    3202          15 :   Node* global_node = graph->V8Node(*global_object_pointer);
    3203             : 
    3204          15 :   CHECK_GE(context->counter, 0);
    3205          15 :   CHECK_LE(context->counter, 1);
    3206          15 :   switch (context->counter++) {
    3207             :     case 0: {
    3208          20 :       Node* embedder_node_A = graph->AddNode(
    3209          20 :           std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeA", 10)));
    3210          10 :       graph->AddEdge(global_node, embedder_node_A);
    3211          10 :       break;
    3212             :     }
    3213             :     case 1: {
    3214          10 :       Node* embedder_node_B = graph->AddNode(
    3215          10 :           std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeB", 20)));
    3216           5 :       graph->AddEdge(global_node, embedder_node_B);
    3217           5 :       break;
    3218             :     }
    3219             :   }
    3220          15 : }
    3221             : 
    3222       26644 : TEST(EmbedderGraphMultipleCallbacks) {
    3223           5 :   i::FLAG_heap_profiler_use_embedder_graph = true;
    3224           5 :   LocalContext env;
    3225          10 :   v8::HandleScope scope(env->GetIsolate());
    3226           5 :   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
    3227             :   v8::Local<v8::Value> global_object =
    3228             :       v8::Utils::ToLocal(i::Handle<i::JSObject>(
    3229          10 :           (isolate->context()->native_context()->global_object()), isolate));
    3230           5 :   global_object_pointer = &global_object;
    3231           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3232           5 :   GraphBuildingContext context;
    3233             : 
    3234             :   heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
    3235           5 :                                                &context);
    3236             :   heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
    3237           5 :                                                &context);
    3238           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    3239           5 :   CHECK_EQ(context.counter, 2);
    3240           5 :   CHECK(ValidateSnapshot(snapshot));
    3241           5 :   CheckEmbedderGraphSnapshotWithContext(env->GetIsolate(), snapshot, &context);
    3242             : 
    3243             :   heap_profiler->RemoveBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
    3244           5 :                                                   &context);
    3245           5 :   context.counter = 0;
    3246             : 
    3247           5 :   snapshot = heap_profiler->TakeHeapSnapshot();
    3248           5 :   CHECK_EQ(context.counter, 1);
    3249           5 :   CHECK(ValidateSnapshot(snapshot));
    3250           5 :   CheckEmbedderGraphSnapshotWithContext(env->GetIsolate(), snapshot, &context);
    3251           5 : }
    3252             : 
    3253       26644 : TEST(StrongHandleAnnotation) {
    3254           5 :   LocalContext env;
    3255          10 :   v8::HandleScope scope(env->GetIsolate());
    3256             :   v8::Persistent<v8::Object> handle1, handle2;
    3257          15 :   handle1.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
    3258          15 :   handle2.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
    3259             :   handle1.AnnotateStrongRetainer("my_label");
    3260             :   handle2.AnnotateStrongRetainer("my_label");
    3261           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3262           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    3263             :   const v8::HeapGraphNode* gc_roots = GetRootChild(snapshot, "(GC roots)");
    3264           5 :   CHECK(gc_roots);
    3265             :   const v8::HeapGraphNode* global_handles =
    3266           5 :       GetChildByName(gc_roots, "(Global handles)");
    3267           5 :   CHECK(global_handles);
    3268             :   int found = 0;
    3269          20 :   for (int i = 0, count = global_handles->GetChildrenCount(); i < count; ++i) {
    3270          15 :     const v8::HeapGraphEdge* edge = global_handles->GetChild(i);
    3271          30 :     v8::String::Utf8Value edge_name(CcTest::isolate(), edge->GetName());
    3272          15 :     if (EndsWith(*edge_name, "my_label")) ++found;
    3273             :   }
    3274           5 :   CHECK_EQ(2, found);
    3275           5 : }
    3276             : 
    3277           5 : void BuildEmbedderGraphWithWrapperNode(v8::Isolate* v8_isolate,
    3278             :                                        v8::EmbedderGraph* graph, void* data) {
    3279             :   using Node = v8::EmbedderGraph::Node;
    3280           5 :   Node* global_node = graph->V8Node(*global_object_pointer);
    3281          10 :   Node* wrapper_node = graph->AddNode(
    3282          10 :       std::unique_ptr<Node>(new EmbedderNode("WrapperNode / TAG", 10)));
    3283          10 :   Node* embedder_node = graph->AddNode(std::unique_ptr<Node>(
    3284          10 :       new EmbedderNode("EmbedderNode", 10, wrapper_node)));
    3285             :   Node* other_node =
    3286          15 :       graph->AddNode(std::unique_ptr<Node>(new EmbedderNode("OtherNode", 20)));
    3287           5 :   graph->AddEdge(global_node, embedder_node);
    3288           5 :   graph->AddEdge(wrapper_node, other_node);
    3289             : 
    3290          10 :   Node* wrapper_node2 = graph->AddNode(
    3291          10 :       std::unique_ptr<Node>(new EmbedderNode("WrapperNode2", 10)));
    3292          10 :   Node* embedder_node2 = graph->AddNode(std::unique_ptr<Node>(
    3293          10 :       new EmbedderNode("EmbedderNode2", 10, wrapper_node2)));
    3294           5 :   graph->AddEdge(global_node, embedder_node2);
    3295           5 :   graph->AddEdge(embedder_node2, wrapper_node2);
    3296           5 :   graph->AddEdge(wrapper_node2, other_node);
    3297           5 : }
    3298             : 
    3299       26644 : TEST(EmbedderGraphWithWrapperNode) {
    3300           5 :   i::FLAG_heap_profiler_use_embedder_graph = true;
    3301           5 :   LocalContext env;
    3302          10 :   v8::HandleScope scope(env->GetIsolate());
    3303           5 :   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
    3304             :   v8::Local<v8::Value> global_object =
    3305             :       v8::Utils::ToLocal(i::Handle<i::JSObject>(
    3306          10 :           (isolate->context()->native_context()->global_object()), isolate));
    3307           5 :   global_object_pointer = &global_object;
    3308           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3309             :   heap_profiler->AddBuildEmbedderGraphCallback(
    3310           5 :       BuildEmbedderGraphWithWrapperNode, nullptr);
    3311           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    3312           5 :   CHECK(ValidateSnapshot(snapshot));
    3313           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    3314             :   const v8::HeapGraphNode* embedder_node =
    3315           5 :       GetChildByName(global, "EmbedderNode / TAG");
    3316             :   const v8::HeapGraphNode* other_node =
    3317           5 :       GetChildByName(embedder_node, "OtherNode");
    3318           5 :   CHECK(other_node);
    3319             :   const v8::HeapGraphNode* wrapper_node =
    3320           5 :       GetChildByName(embedder_node, "WrapperNode / TAG");
    3321           5 :   CHECK(!wrapper_node);
    3322             : 
    3323             :   const v8::HeapGraphNode* embedder_node2 =
    3324           5 :       GetChildByName(global, "EmbedderNode2");
    3325           5 :   other_node = GetChildByName(embedder_node2, "OtherNode");
    3326           5 :   CHECK(other_node);
    3327             :   const v8::HeapGraphNode* wrapper_node2 =
    3328           5 :       GetChildByName(embedder_node, "WrapperNode2");
    3329           5 :   CHECK(!wrapper_node2);
    3330           5 : }
    3331             : 
    3332          10 : class EmbedderNodeWithPrefix : public v8::EmbedderGraph::Node {
    3333             :  public:
    3334             :   EmbedderNodeWithPrefix(const char* prefix, const char* name)
    3335           5 :       : prefix_(prefix), name_(name) {}
    3336             : 
    3337             :   // Graph::Node overrides.
    3338           5 :   const char* Name() override { return name_; }
    3339           5 :   size_t SizeInBytes() override { return 0; }
    3340           5 :   const char* NamePrefix() override { return prefix_; }
    3341             : 
    3342             :  private:
    3343             :   const char* prefix_;
    3344             :   const char* name_;
    3345             : };
    3346             : 
    3347           5 : void BuildEmbedderGraphWithPrefix(v8::Isolate* v8_isolate,
    3348             :                                   v8::EmbedderGraph* graph, void* data) {
    3349             :   using Node = v8::EmbedderGraph::Node;
    3350           5 :   Node* global_node = graph->V8Node(*global_object_pointer);
    3351          10 :   Node* node = graph->AddNode(
    3352          10 :       std::unique_ptr<Node>(new EmbedderNodeWithPrefix("Detached", "Node")));
    3353           5 :   graph->AddEdge(global_node, node);
    3354           5 : }
    3355             : 
    3356       26644 : TEST(EmbedderGraphWithPrefix) {
    3357           5 :   i::FLAG_heap_profiler_use_embedder_graph = true;
    3358           5 :   LocalContext env;
    3359          10 :   v8::HandleScope scope(env->GetIsolate());
    3360           5 :   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
    3361             :   v8::Local<v8::Value> global_object =
    3362             :       v8::Utils::ToLocal(i::Handle<i::JSObject>(
    3363          10 :           (isolate->context()->native_context()->global_object()), isolate));
    3364           5 :   global_object_pointer = &global_object;
    3365           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3366             :   heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithPrefix,
    3367           5 :                                                nullptr);
    3368           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    3369           5 :   CHECK(ValidateSnapshot(snapshot));
    3370           5 :   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    3371           5 :   const v8::HeapGraphNode* node = GetChildByName(global, "Detached Node");
    3372           5 :   CHECK(node);
    3373           5 : }
    3374             : 
    3375             : static inline i::Address ToAddress(int n) { return static_cast<i::Address>(n); }
    3376             : 
    3377       26644 : TEST(AddressToTraceMap) {
    3378             :   i::AddressToTraceMap map;
    3379             : 
    3380           5 :   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(150)));
    3381             : 
    3382             :   // [0x100, 0x200) -> 1
    3383           5 :   map.AddRange(ToAddress(0x100), 0x100, 1U);
    3384           5 :   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x50)));
    3385           5 :   CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x100)));
    3386           5 :   CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x150)));
    3387           5 :   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x100 + 0x100)));
    3388           5 :   CHECK_EQ(1u, map.size());
    3389             : 
    3390             :   // [0x100, 0x200) -> 1, [0x200, 0x300) -> 2
    3391           5 :   map.AddRange(ToAddress(0x200), 0x100, 2U);
    3392           5 :   CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x2A0)));
    3393           5 :   CHECK_EQ(2u, map.size());
    3394             : 
    3395             :   // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2
    3396           5 :   map.AddRange(ToAddress(0x180), 0x100, 3U);
    3397           5 :   CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
    3398           5 :   CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
    3399           5 :   CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
    3400           5 :   CHECK_EQ(3u, map.size());
    3401             : 
    3402             :   // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2,
    3403             :   // [0x400, 0x500) -> 4
    3404           5 :   map.AddRange(ToAddress(0x400), 0x100, 4U);
    3405           5 :   CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
    3406           5 :   CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
    3407           5 :   CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
    3408           5 :   CHECK_EQ(4u, map.GetTraceNodeId(ToAddress(0x450)));
    3409           5 :   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x500)));
    3410           5 :   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x350)));
    3411           5 :   CHECK_EQ(4u, map.size());
    3412             : 
    3413             :   // [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5
    3414           5 :   map.AddRange(ToAddress(0x200), 0x400, 5U);
    3415           5 :   CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
    3416           5 :   CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x400)));
    3417           5 :   CHECK_EQ(3u, map.size());
    3418             : 
    3419             :   // [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5
    3420           5 :   map.AddRange(ToAddress(0x180), 0x80, 6U);
    3421           5 :   map.AddRange(ToAddress(0x180), 0x80, 7U);
    3422           5 :   CHECK_EQ(7u, map.GetTraceNodeId(ToAddress(0x180)));
    3423           5 :   CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
    3424           5 :   CHECK_EQ(3u, map.size());
    3425             : 
    3426           5 :   map.Clear();
    3427           5 :   CHECK_EQ(0u, map.size());
    3428           5 :   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x400)));
    3429           5 : }
    3430             : 
    3431          44 : static const v8::AllocationProfile::Node* FindAllocationProfileNode(
    3432             :     v8::Isolate* isolate, v8::AllocationProfile& profile,
    3433             :     const Vector<const char*>& names) {
    3434          44 :   v8::AllocationProfile::Node* node = profile.GetRootNode();
    3435         394 :   for (int i = 0; node != nullptr && i < names.length(); ++i) {
    3436         204 :     const char* name = names[i];
    3437         102 :     auto children = node->children;
    3438             :     node = nullptr;
    3439         106 :     for (v8::AllocationProfile::Node* child : children) {
    3440         110 :       v8::String::Utf8Value child_name(isolate, child->name);
    3441         106 :       if (strcmp(*child_name, name) == 0) {
    3442             :         node = child;
    3443         102 :         break;
    3444             :       }
    3445             :     }
    3446             :   }
    3447          44 :   return node;
    3448             : }
    3449             : 
    3450          24 : static void CheckNoZeroCountNodes(v8::AllocationProfile::Node* node) {
    3451          88 :   for (auto alloc : node->allocations) {
    3452          64 :     CHECK_GT(alloc.count, 0u);
    3453             :   }
    3454          44 :   for (auto child : node->children) {
    3455          20 :     CheckNoZeroCountNodes(child);
    3456             :   }
    3457          24 : }
    3458             : 
    3459             : static int NumberOfAllocations(const v8::AllocationProfile::Node* node) {
    3460             :   int count = 0;
    3461          90 :   for (auto allocation : node->allocations) {
    3462          70 :     count += allocation.count;
    3463             :   }
    3464             :   return count;
    3465             : }
    3466             : 
    3467             : static const char* simple_sampling_heap_profiler_script =
    3468             :     "var A = [];\n"
    3469             :     "function bar(size) { return new Array(size); }\n"
    3470             :     "var foo = function() {\n"
    3471             :     "  for (var i = 0; i < 1024; ++i) {\n"
    3472             :     "    A[i] = bar(1024);\n"
    3473             :     "  }\n"
    3474             :     "}\n"
    3475             :     "foo();";
    3476             : 
    3477       26643 : TEST(SamplingHeapProfiler) {
    3478           8 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    3479           4 :   LocalContext env;
    3480           4 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3481             : 
    3482             :   // Turn off always_opt. Inlining can cause stack traces to be shorter than
    3483             :   // what we expect in this test.
    3484           4 :   v8::internal::FLAG_always_opt = false;
    3485             : 
    3486             :   // Suppress randomness to avoid flakiness in tests.
    3487           4 :   v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
    3488             : 
    3489             :   // Sample should be empty if requested before sampling has started.
    3490             :   {
    3491           4 :     v8::AllocationProfile* profile = heap_profiler->GetAllocationProfile();
    3492           4 :     CHECK_NULL(profile);
    3493             :   }
    3494             : 
    3495             :   {
    3496           4 :     heap_profiler->StartSamplingHeapProfiler(1024);
    3497           4 :     CompileRun(simple_sampling_heap_profiler_script);
    3498             : 
    3499             :     std::unique_ptr<v8::AllocationProfile> profile(
    3500           4 :         heap_profiler->GetAllocationProfile());
    3501           4 :     CHECK(profile);
    3502             : 
    3503           4 :     const char* names[] = {"", "foo", "bar"};
    3504           4 :     auto node_bar = FindAllocationProfileNode(env->GetIsolate(), *profile,
    3505           8 :                                               ArrayVector(names));
    3506           4 :     CHECK(node_bar);
    3507             : 
    3508           4 :     heap_profiler->StopSamplingHeapProfiler();
    3509             :   }
    3510             : 
    3511             :   // Samples should get cleared once sampling is stopped.
    3512             :   {
    3513           4 :     v8::AllocationProfile* profile = heap_profiler->GetAllocationProfile();
    3514           4 :     CHECK_NULL(profile);
    3515             :   }
    3516             : 
    3517             :   // A more complicated test cases with deeper call graph and dynamically
    3518             :   // generated function names.
    3519             :   {
    3520           4 :     heap_profiler->StartSamplingHeapProfiler(64);
    3521           4 :     CompileRun(record_trace_tree_source);
    3522             : 
    3523             :     std::unique_ptr<v8::AllocationProfile> profile(
    3524           4 :         heap_profiler->GetAllocationProfile());
    3525           4 :     CHECK(profile);
    3526             : 
    3527           4 :     const char* names1[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
    3528           4 :     auto node1 = FindAllocationProfileNode(env->GetIsolate(), *profile,
    3529           8 :                                            ArrayVector(names1));
    3530           4 :     CHECK(node1);
    3531             : 
    3532           4 :     const char* names2[] = {"", "generateFunctions"};
    3533           4 :     auto node2 = FindAllocationProfileNode(env->GetIsolate(), *profile,
    3534           8 :                                            ArrayVector(names2));
    3535           4 :     CHECK(node2);
    3536             : 
    3537           4 :     heap_profiler->StopSamplingHeapProfiler();
    3538             :   }
    3539             : 
    3540             :   // A test case with scripts unloaded before profile gathered
    3541             :   {
    3542           4 :     heap_profiler->StartSamplingHeapProfiler(64);
    3543             :     CompileRun(
    3544             :         "for (var i = 0; i < 1024; i++) {\n"
    3545             :         "  eval(\"new Array(100)\");\n"
    3546             :         "}\n");
    3547             : 
    3548           4 :     CcTest::CollectAllGarbage();
    3549             : 
    3550             :     std::unique_ptr<v8::AllocationProfile> profile(
    3551           4 :         heap_profiler->GetAllocationProfile());
    3552           4 :     CHECK(profile);
    3553             : 
    3554           4 :     CheckNoZeroCountNodes(profile->GetRootNode());
    3555             : 
    3556           4 :     heap_profiler->StopSamplingHeapProfiler();
    3557             :   }
    3558           4 : }
    3559             : 
    3560       26644 : TEST(SamplingHeapProfilerRateAgnosticEstimates) {
    3561          10 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    3562           5 :   LocalContext env;
    3563           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3564             : 
    3565             :   // Turn off always_opt. Inlining can cause stack traces to be shorter than
    3566             :   // what we expect in this test.
    3567           5 :   v8::internal::FLAG_always_opt = false;
    3568             : 
    3569             :   // Disable compilation cache to force compilation in both cases
    3570           5 :   v8::internal::FLAG_compilation_cache = false;
    3571             : 
    3572             :   // Suppress randomness to avoid flakiness in tests.
    3573           5 :   v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
    3574             : 
    3575             :   // stress_incremental_marking adds randomness to the test.
    3576           5 :   v8::internal::FLAG_stress_incremental_marking = false;
    3577             : 
    3578             :   // warmup compilation
    3579           5 :   CompileRun(simple_sampling_heap_profiler_script);
    3580             : 
    3581           5 :   int count_1024 = 0;
    3582             :   {
    3583           5 :     heap_profiler->StartSamplingHeapProfiler(1024);
    3584           5 :     CompileRun(simple_sampling_heap_profiler_script);
    3585             : 
    3586             :     std::unique_ptr<v8::AllocationProfile> profile(
    3587           5 :         heap_profiler->GetAllocationProfile());
    3588           5 :     CHECK(profile);
    3589             : 
    3590           5 :     const char* path_to_foo[] = {"", "foo"};
    3591           5 :     auto node_foo = FindAllocationProfileNode(env->GetIsolate(), *profile,
    3592          10 :                                               ArrayVector(path_to_foo));
    3593           5 :     CHECK(node_foo);
    3594           5 :     const char* path_to_bar[] = {"", "foo", "bar"};
    3595           5 :     auto node_bar = FindAllocationProfileNode(env->GetIsolate(), *profile,
    3596          10 :                                               ArrayVector(path_to_bar));
    3597           5 :     CHECK(node_bar);
    3598             : 
    3599             :     // Function bar can be inlined in foo.
    3600           5 :     count_1024 = NumberOfAllocations(node_foo) + NumberOfAllocations(node_bar);
    3601             : 
    3602           5 :     heap_profiler->StopSamplingHeapProfiler();
    3603             :   }
    3604             : 
    3605             :   // Sampling at a higher rate should give us similar numbers of objects.
    3606             :   {
    3607           5 :     heap_profiler->StartSamplingHeapProfiler(128);
    3608           5 :     CompileRun(simple_sampling_heap_profiler_script);
    3609             : 
    3610             :     std::unique_ptr<v8::AllocationProfile> profile(
    3611           5 :         heap_profiler->GetAllocationProfile());
    3612           5 :     CHECK(profile);
    3613             : 
    3614           5 :     const char* path_to_foo[] = {"", "foo"};
    3615           5 :     auto node_foo = FindAllocationProfileNode(env->GetIsolate(), *profile,
    3616          10 :                                               ArrayVector(path_to_foo));
    3617           5 :     CHECK(node_foo);
    3618           5 :     const char* path_to_bar[] = {"", "foo", "bar"};
    3619           5 :     auto node_bar = FindAllocationProfileNode(env->GetIsolate(), *profile,
    3620          10 :                                               ArrayVector(path_to_bar));
    3621           5 :     CHECK(node_bar);
    3622             : 
    3623             :     // Function bar can be inlined in foo.
    3624             :     int count_128 =
    3625           5 :         NumberOfAllocations(node_foo) + NumberOfAllocations(node_bar);
    3626             : 
    3627             :     // We should have similar unsampled counts of allocations. Though
    3628             :     // we will sample different numbers of objects at different rates,
    3629             :     // the unsampling process should produce similar final estimates
    3630             :     // at the true number of allocations. However, the process to
    3631             :     // determine these unsampled counts is probabilisitic so we need to
    3632             :     // account for error.
    3633           5 :     double max_count = std::max(count_128, count_1024);
    3634           5 :     double min_count = std::min(count_128, count_1024);
    3635           5 :     double percent_difference = (max_count - min_count) / min_count;
    3636           5 :     CHECK_LT(percent_difference, 0.1);
    3637             : 
    3638           5 :     heap_profiler->StopSamplingHeapProfiler();
    3639             :   }
    3640           5 : }
    3641             : 
    3642       26644 : TEST(SamplingHeapProfilerApiAllocation) {
    3643          10 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    3644           5 :   LocalContext env;
    3645           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3646             : 
    3647             :   // Suppress randomness to avoid flakiness in tests.
    3648           5 :   v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
    3649             : 
    3650           5 :   heap_profiler->StartSamplingHeapProfiler(256);
    3651             : 
    3652       81925 :   for (int i = 0; i < 8 * 1024; ++i) v8::Object::New(env->GetIsolate());
    3653             : 
    3654             :   std::unique_ptr<v8::AllocationProfile> profile(
    3655           5 :       heap_profiler->GetAllocationProfile());
    3656           5 :   CHECK(profile);
    3657           5 :   const char* names[] = {"(V8 API)"};
    3658           5 :   auto node = FindAllocationProfileNode(env->GetIsolate(), *profile,
    3659          10 :                                         ArrayVector(names));
    3660           5 :   CHECK(node);
    3661             : 
    3662           5 :   heap_profiler->StopSamplingHeapProfiler();
    3663           5 : }
    3664             : 
    3665       26644 : TEST(SamplingHeapProfilerApiSamples) {
    3666          10 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    3667           5 :   LocalContext env;
    3668           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3669             : 
    3670             :   // Suppress randomness to avoid flakiness in tests.
    3671           5 :   v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
    3672             : 
    3673           5 :   heap_profiler->StartSamplingHeapProfiler(1024);
    3674             : 
    3675             :   size_t count = 8 * 1024;
    3676       81925 :   for (size_t i = 0; i < count; ++i) v8::Object::New(env->GetIsolate());
    3677             : 
    3678             :   std::unique_ptr<v8::AllocationProfile> profile(
    3679           5 :       heap_profiler->GetAllocationProfile());
    3680           5 :   CHECK(profile);
    3681             : 
    3682             :   std::vector<v8::AllocationProfile::Node*> nodes_to_visit;
    3683             :   std::unordered_set<uint32_t> node_ids;
    3684          10 :   nodes_to_visit.push_back(profile->GetRootNode());
    3685          25 :   while (!nodes_to_visit.empty()) {
    3686          10 :     v8::AllocationProfile::Node* node = nodes_to_visit.back();
    3687             :     nodes_to_visit.pop_back();
    3688          10 :     CHECK_LT(0, node->node_id);
    3689          10 :     CHECK_EQ(0, node_ids.count(node->node_id));
    3690          10 :     node_ids.insert(node->node_id);
    3691             :     nodes_to_visit.insert(nodes_to_visit.end(), node->children.begin(),
    3692          10 :                           node->children.end());
    3693             :   }
    3694             : 
    3695             :   size_t total_size = 0;
    3696             :   std::unordered_set<uint64_t> samples_set;
    3697           5 :   for (auto& sample : profile->GetSamples()) {
    3698        2155 :     total_size += sample.size * sample.count;
    3699        2155 :     CHECK_EQ(0, samples_set.count(sample.sample_id));
    3700        2155 :     CHECK_EQ(1, node_ids.count(sample.node_id));
    3701        2155 :     CHECK_GT(sample.node_id, 0);
    3702        2155 :     CHECK_GT(sample.sample_id, 0);
    3703        2155 :     samples_set.insert(sample.sample_id);
    3704             :   }
    3705             :   size_t object_size = total_size / count;
    3706           5 :   CHECK_GE(object_size, sizeof(void*) * 2);
    3707           5 :   heap_profiler->StopSamplingHeapProfiler();
    3708           5 : }
    3709             : 
    3710       26644 : TEST(SamplingHeapProfilerLeftTrimming) {
    3711          10 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    3712           5 :   LocalContext env;
    3713           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3714             : 
    3715             :   // Suppress randomness to avoid flakiness in tests.
    3716           5 :   v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
    3717             : 
    3718           5 :   heap_profiler->StartSamplingHeapProfiler(64);
    3719             : 
    3720             :   CompileRun(
    3721             :       "for (var j = 0; j < 500; ++j) {\n"
    3722             :       "  var a = [];\n"
    3723             :       "  for (var i = 0; i < 5; ++i)\n"
    3724             :       "      a[i] = i;\n"
    3725             :       "  for (var i = 0; i < 3; ++i)\n"
    3726             :       "      a.shift();\n"
    3727             :       "}\n");
    3728             : 
    3729           5 :   CcTest::CollectGarbage(v8::internal::NEW_SPACE);
    3730             :   // Should not crash.
    3731             : 
    3732           5 :   heap_profiler->StopSamplingHeapProfiler();
    3733           5 : }
    3734             : 
    3735       26644 : TEST(SamplingHeapProfilerPretenuredInlineAllocations) {
    3736           5 :   i::FLAG_allow_natives_syntax = true;
    3737           5 :   i::FLAG_expose_gc = true;
    3738             : 
    3739           5 :   CcTest::InitializeVM();
    3740           8 :   if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
    3741           3 :   if (i::FLAG_gc_global || i::FLAG_stress_compaction ||
    3742             :       i::FLAG_stress_incremental_marking) {
    3743             :     return;
    3744             :   }
    3745             : 
    3746           4 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    3747           2 :   LocalContext env;
    3748           2 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3749             : 
    3750             :   // Suppress randomness to avoid flakiness in tests.
    3751           2 :   v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
    3752             : 
    3753             :   // Grow new space until maximum capacity reached.
    3754          18 :   while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
    3755           8 :     CcTest::heap()->new_space()->Grow();
    3756             :   }
    3757             : 
    3758             :   i::ScopedVector<char> source(1024);
    3759             :   i::SNPrintF(source,
    3760             :               "var number_elements = %d;"
    3761             :               "var elements = new Array(number_elements);"
    3762             :               "function f() {"
    3763             :               "  for (var i = 0; i < number_elements; i++) {"
    3764             :               "    elements[i] = [{}, {}, {}];"
    3765             :               "  }"
    3766             :               "  return elements[number_elements - 1];"
    3767             :               "};"
    3768             :               "f(); gc();"
    3769             :               "f(); f();"
    3770             :               "%%OptimizeFunctionOnNextCall(f);"
    3771             :               "f();"
    3772             :               "f;",
    3773           2 :               i::AllocationSite::kPretenureMinimumCreated + 1);
    3774             : 
    3775             :   v8::Local<v8::Function> f =
    3776             :       v8::Local<v8::Function>::Cast(CompileRun(source.start()));
    3777             : 
    3778             :   // Make sure the function is producing pre-tenured objects.
    3779           6 :   auto res = f->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked();
    3780             :   i::Handle<i::JSObject> o = i::Handle<i::JSObject>::cast(
    3781             :       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
    3782           4 :   CHECK(CcTest::heap()->InOldSpace(o->elements()));
    3783           4 :   CHECK(CcTest::heap()->InOldSpace(*o));
    3784             : 
    3785             :   // Call the function and profile it.
    3786           2 :   heap_profiler->StartSamplingHeapProfiler(64);
    3787         322 :   for (int i = 0; i < 80; ++i) {
    3788         480 :     f->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked();
    3789             :   }
    3790             : 
    3791             :   std::unique_ptr<v8::AllocationProfile> profile(
    3792           2 :       heap_profiler->GetAllocationProfile());
    3793           2 :   CHECK(profile);
    3794           2 :   heap_profiler->StopSamplingHeapProfiler();
    3795             : 
    3796           2 :   const char* names[] = {"f"};
    3797           2 :   auto node_f = FindAllocationProfileNode(env->GetIsolate(), *profile,
    3798           4 :                                           ArrayVector(names));
    3799           2 :   CHECK(node_f);
    3800             : 
    3801             :   int count = 0;
    3802           4 :   for (auto allocation : node_f->allocations) {
    3803           2 :     count += allocation.count;
    3804             :   }
    3805             : 
    3806           2 :   CHECK_GE(count, 8000);
    3807             : }
    3808             : 
    3809       26644 : TEST(SamplingHeapProfilerLargeInterval) {
    3810          10 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    3811           5 :   LocalContext env;
    3812           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3813             : 
    3814             :   // Suppress randomness to avoid flakiness in tests.
    3815           5 :   v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
    3816             : 
    3817           5 :   heap_profiler->StartSamplingHeapProfiler(512 * 1024);
    3818             : 
    3819       81925 :   for (int i = 0; i < 8 * 1024; ++i) {
    3820       40960 :     CcTest::i_isolate()->factory()->NewFixedArray(1024);
    3821             :   }
    3822             : 
    3823             :   std::unique_ptr<v8::AllocationProfile> profile(
    3824           5 :       heap_profiler->GetAllocationProfile());
    3825           5 :   CHECK(profile);
    3826           5 :   const char* names[] = {"(EXTERNAL)"};
    3827           5 :   auto node = FindAllocationProfileNode(env->GetIsolate(), *profile,
    3828          10 :                                         ArrayVector(names));
    3829           5 :   CHECK(node);
    3830             : 
    3831           5 :   heap_profiler->StopSamplingHeapProfiler();
    3832           5 : }
    3833             : 
    3834       26644 : TEST(HeapSnapshotPrototypeNotJSReceiver) {
    3835           5 :   LocalContext env;
    3836          10 :   v8::HandleScope scope(env->GetIsolate());
    3837           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3838             :   CompileRun(
    3839             :       "function object() {}"
    3840             :       "object.prototype = 42;");
    3841           5 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    3842           5 :   CHECK(ValidateSnapshot(snapshot));
    3843           5 : }
    3844             : 
    3845       26644 : TEST(SamplingHeapProfilerSampleDuringDeopt) {
    3846           5 :   i::FLAG_allow_natives_syntax = true;
    3847             : 
    3848          10 :   v8::HandleScope scope(v8::Isolate::GetCurrent());
    3849           5 :   LocalContext env;
    3850           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3851             : 
    3852             :   // Suppress randomness to avoid flakiness in tests.
    3853           5 :   v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
    3854             : 
    3855             :   // Small sample interval to force each object to be sampled.
    3856           5 :   heap_profiler->StartSamplingHeapProfiler(i::kTaggedSize);
    3857             : 
    3858             :   // Lazy deopt from runtime call from inlined callback function.
    3859             :   const char* source =
    3860             :       "var b = "
    3861             :       "  [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];"
    3862             :       "(function f() {"
    3863             :       "  var result = 0;"
    3864             :       "  var lazyDeopt = function(deopt) {"
    3865             :       "    var callback = function(v,i,o) {"
    3866             :       "      result += i;"
    3867             :       "      if (i == 13 && deopt) {"
    3868             :       "          %DeoptimizeNow();"
    3869             :       "      }"
    3870             :       "      return v;"
    3871             :       "    };"
    3872             :       "    b.map(callback);"
    3873             :       "  };"
    3874             :       "  lazyDeopt();"
    3875             :       "  lazyDeopt();"
    3876             :       "  %OptimizeFunctionOnNextCall(lazyDeopt);"
    3877             :       "  lazyDeopt();"
    3878             :       "  lazyDeopt(true);"
    3879             :       "  lazyDeopt();"
    3880             :       "})();";
    3881             : 
    3882             :   CompileRun(source);
    3883             :   // Should not crash.
    3884             : 
    3885             :   std::unique_ptr<v8::AllocationProfile> profile(
    3886           5 :       heap_profiler->GetAllocationProfile());
    3887           5 :   CHECK(profile);
    3888           5 :   heap_profiler->StopSamplingHeapProfiler();
    3889           5 : }
    3890             : 
    3891       26643 : TEST(WeakReference) {
    3892           4 :   v8::Isolate* isolate = CcTest::isolate();
    3893             :   i::Isolate* i_isolate = CcTest::i_isolate();
    3894             :   i::Factory* factory = i_isolate->factory();
    3895             :   i::HandleScope scope(i_isolate);
    3896           4 :   LocalContext env;
    3897             : 
    3898             :   // Create a FeedbackVector.
    3899             :   v8::Local<v8::Script> script =
    3900           4 :       v8::Script::Compile(isolate->GetCurrentContext(),
    3901           4 :                           v8::String::NewFromUtf8(isolate, "function foo() {}",
    3902             :                                                   v8::NewStringType::kNormal)
    3903           4 :                               .ToLocalChecked())
    3904             :           .ToLocalChecked();
    3905           4 :   v8::MaybeLocal<v8::Value> value = script->Run(isolate->GetCurrentContext());
    3906           4 :   CHECK(!value.IsEmpty());
    3907             : 
    3908             :   i::Handle<i::Object> obj = v8::Utils::OpenHandle(*script);
    3909             :   i::Handle<i::SharedFunctionInfo> shared_function =
    3910             :       i::Handle<i::SharedFunctionInfo>(i::JSFunction::cast(*obj)->shared(),
    3911             :                                        i_isolate);
    3912             :   i::Handle<i::ClosureFeedbackCellArray> feedback_cell_array =
    3913           4 :       i::ClosureFeedbackCellArray::New(i_isolate, shared_function);
    3914             :   i::Handle<i::FeedbackVector> fv =
    3915           4 :       factory->NewFeedbackVector(shared_function, feedback_cell_array);
    3916             : 
    3917             :   // Create a Code.
    3918          16 :   i::Assembler assm(i::AssemblerOptions{});
    3919           4 :   assm.nop();  // supported on all architectures
    3920           4 :   i::CodeDesc desc;
    3921             :   assm.GetCode(i_isolate, &desc);
    3922             :   i::Handle<i::Code> code =
    3923           8 :       factory->NewCode(desc, i::Code::STUB, i::Handle<i::Code>());
    3924           4 :   CHECK(code->IsCode());
    3925             : 
    3926           8 :   fv->set_optimized_code_weak_or_smi(i::HeapObjectReference::Weak(*code));
    3927             : 
    3928           4 :   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
    3929           4 :   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
    3930           4 :   CHECK(ValidateSnapshot(snapshot));
    3931           4 : }
    3932             : 
    3933       26644 : TEST(Bug8373_1) {
    3934           5 :   LocalContext env;
    3935          10 :   v8::HandleScope scope(env->GetIsolate());
    3936           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3937             : 
    3938           5 :   heap_profiler->StartSamplingHeapProfiler(100);
    3939             : 
    3940           5 :   heap_profiler->TakeHeapSnapshot();
    3941             :   // Causes the StringsStorage to be deleted.
    3942           5 :   heap_profiler->DeleteAllHeapSnapshots();
    3943             : 
    3944             :   // Triggers an allocation sample that tries to use the StringsStorage.
    3945       20485 :   for (int i = 0; i < 2 * 1024; ++i) {
    3946             :     CompileRun(
    3947             :         "new Array(64);"
    3948             :         "new Uint8Array(16);");
    3949             :   }
    3950             : 
    3951           5 :   heap_profiler->StopSamplingHeapProfiler();
    3952           5 : }
    3953             : 
    3954       26644 : TEST(Bug8373_2) {
    3955           5 :   LocalContext env;
    3956          10 :   v8::HandleScope scope(env->GetIsolate());
    3957           5 :   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
    3958             : 
    3959           5 :   heap_profiler->StartTrackingHeapObjects(true);
    3960             : 
    3961           5 :   heap_profiler->TakeHeapSnapshot();
    3962             :   // Causes the StringsStorage to be deleted.
    3963           5 :   heap_profiler->DeleteAllHeapSnapshots();
    3964             : 
    3965             :   // Triggers an allocations that try to use the StringsStorage.
    3966       20485 :   for (int i = 0; i < 2 * 1024; ++i) {
    3967             :     CompileRun(
    3968             :         "new Array(64);"
    3969             :         "new Uint8Array(16);");
    3970             :   }
    3971             : 
    3972           5 :   heap_profiler->StopTrackingHeapObjects();
    3973       79922 : }

Generated by: LCOV version 1.10