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