LCOV - code coverage report
Current view: top level - test/cctest - test-heap-profiler.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 2070 2124 97.5 %
Date: 2019-01-20 Functions: 143 164 87.2 %

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

Generated by: LCOV version 1.10