Home | History | Annotate | Download | only in cctest
      1 // Copyright 2009 the V8 project authors. All rights reserved.
      2 //
      3 // Tests for heap profiler
      4 
      5 #ifdef ENABLE_LOGGING_AND_PROFILING
      6 
      7 #include "v8.h"
      8 #include "heap-profiler.h"
      9 #include "snapshot.h"
     10 #include "string-stream.h"
     11 #include "cctest.h"
     12 #include "zone-inl.h"
     13 #include "../include/v8-profiler.h"
     14 
     15 namespace i = v8::internal;
     16 using i::ClustersCoarser;
     17 using i::JSObjectsCluster;
     18 using i::JSObjectsRetainerTree;
     19 using i::JSObjectsClusterTree;
     20 using i::RetainerHeapProfile;
     21 
     22 
     23 namespace {
     24 
     25 class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile {
     26  public:
     27   ConstructorHeapProfileTestHelper()
     28     : i::ConstructorHeapProfile(),
     29       f_name_(FACTORY->NewStringFromAscii(i::CStrVector("F"))),
     30       f_count_(0) {
     31   }
     32 
     33   void Call(const JSObjectsCluster& cluster,
     34             const i::NumberAndSizeInfo& number_and_size) {
     35     if (f_name_->Equals(cluster.constructor())) {
     36       CHECK_EQ(f_count_, 0);
     37       f_count_ = number_and_size.number();
     38       CHECK_GT(f_count_, 0);
     39     }
     40   }
     41 
     42   int f_count() { return f_count_; }
     43 
     44  private:
     45   i::Handle<i::String> f_name_;
     46   int f_count_;
     47 };
     48 
     49 }  // namespace
     50 
     51 
     52 TEST(ConstructorProfile) {
     53   v8::HandleScope scope;
     54   LocalContext env;
     55 
     56   CompileRun(
     57       "function F() {}  // A constructor\n"
     58       "var f1 = new F();\n"
     59       "var f2 = new F();\n");
     60 
     61   ConstructorHeapProfileTestHelper cons_profile;
     62   i::AssertNoAllocation no_alloc;
     63   i::HeapIterator iterator;
     64   for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next())
     65     cons_profile.CollectStats(obj);
     66   CHECK_EQ(0, cons_profile.f_count());
     67   cons_profile.PrintStats();
     68   CHECK_EQ(2, cons_profile.f_count());
     69 }
     70 
     71 
     72 static JSObjectsCluster AddHeapObjectToTree(JSObjectsRetainerTree* tree,
     73                                             i::String* constructor,
     74                                             int instance,
     75                                             JSObjectsCluster* ref1 = NULL,
     76                                             JSObjectsCluster* ref2 = NULL,
     77                                             JSObjectsCluster* ref3 = NULL) {
     78   JSObjectsCluster o(constructor, reinterpret_cast<i::Object*>(instance));
     79   JSObjectsClusterTree* o_tree = new JSObjectsClusterTree();
     80   JSObjectsClusterTree::Locator o_loc;
     81   if (ref1 != NULL) o_tree->Insert(*ref1, &o_loc);
     82   if (ref2 != NULL) o_tree->Insert(*ref2, &o_loc);
     83   if (ref3 != NULL) o_tree->Insert(*ref3, &o_loc);
     84   JSObjectsRetainerTree::Locator loc;
     85   tree->Insert(o, &loc);
     86   loc.set_value(o_tree);
     87   return o;
     88 }
     89 
     90 
     91 static void AddSelfReferenceToTree(JSObjectsRetainerTree* tree,
     92                                    JSObjectsCluster* self_ref) {
     93   JSObjectsRetainerTree::Locator loc;
     94   CHECK(tree->Find(*self_ref, &loc));
     95   JSObjectsClusterTree::Locator o_loc;
     96   CHECK_NE(NULL, loc.value());
     97   loc.value()->Insert(*self_ref, &o_loc);
     98 }
     99 
    100 
    101 static inline void CheckEqualsHelper(const char* file, int line,
    102                                      const char* expected_source,
    103                                      const JSObjectsCluster& expected,
    104                                      const char* value_source,
    105                                      const JSObjectsCluster& value) {
    106   if (JSObjectsCluster::Compare(expected, value) != 0) {
    107     i::HeapStringAllocator allocator;
    108     i::StringStream stream(&allocator);
    109     stream.Add("#  Expected: ");
    110     expected.DebugPrint(&stream);
    111     stream.Add("\n#  Found: ");
    112     value.DebugPrint(&stream);
    113     V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n%s",
    114              expected_source, value_source,
    115              *stream.ToCString());
    116   }
    117 }
    118 
    119 
    120 static inline void CheckNonEqualsHelper(const char* file, int line,
    121                                      const char* expected_source,
    122                                      const JSObjectsCluster& expected,
    123                                      const char* value_source,
    124                                      const JSObjectsCluster& value) {
    125   if (JSObjectsCluster::Compare(expected, value) == 0) {
    126     i::HeapStringAllocator allocator;
    127     i::StringStream stream(&allocator);
    128     stream.Add("# !Expected: ");
    129     expected.DebugPrint(&stream);
    130     stream.Add("\n#  Found: ");
    131     value.DebugPrint(&stream);
    132     V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n%s",
    133              expected_source, value_source,
    134              *stream.ToCString());
    135   }
    136 }
    137 
    138 
    139 TEST(ClustersCoarserSimple) {
    140   v8::HandleScope scope;
    141   LocalContext env;
    142 
    143   i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
    144 
    145   JSObjectsRetainerTree tree;
    146   JSObjectsCluster function(HEAP->function_class_symbol());
    147   JSObjectsCluster a(*FACTORY->NewStringFromAscii(i::CStrVector("A")));
    148   JSObjectsCluster b(*FACTORY->NewStringFromAscii(i::CStrVector("B")));
    149 
    150   // o1 <- Function
    151   JSObjectsCluster o1 =
    152       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100, &function);
    153   // o2 <- Function
    154   JSObjectsCluster o2 =
    155       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x200, &function);
    156   // o3 <- A, B
    157   JSObjectsCluster o3 =
    158       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &a, &b);
    159   // o4 <- B, A
    160   JSObjectsCluster o4 =
    161       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x400, &b, &a);
    162   // o5 <- A, B, Function
    163   JSObjectsCluster o5 =
    164       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x500,
    165                           &a, &b, &function);
    166 
    167   ClustersCoarser coarser;
    168   coarser.Process(&tree);
    169 
    170   CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2));
    171   CHECK_EQ(coarser.GetCoarseEquivalent(o3), coarser.GetCoarseEquivalent(o4));
    172   CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o3));
    173   CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o5));
    174 }
    175 
    176 
    177 TEST(ClustersCoarserMultipleConstructors) {
    178   v8::HandleScope scope;
    179   LocalContext env;
    180 
    181   i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
    182 
    183   JSObjectsRetainerTree tree;
    184   JSObjectsCluster function(HEAP->function_class_symbol());
    185 
    186   // o1 <- Function
    187   JSObjectsCluster o1 =
    188       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100, &function);
    189   // a1 <- Function
    190   JSObjectsCluster a1 =
    191       AddHeapObjectToTree(&tree, HEAP->Array_symbol(), 0x1000, &function);
    192   // o2 <- Function
    193   JSObjectsCluster o2 =
    194       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x200, &function);
    195   // a2 <- Function
    196   JSObjectsCluster a2 =
    197       AddHeapObjectToTree(&tree, HEAP->Array_symbol(), 0x2000, &function);
    198 
    199   ClustersCoarser coarser;
    200   coarser.Process(&tree);
    201 
    202   CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2));
    203   CHECK_EQ(coarser.GetCoarseEquivalent(a1), coarser.GetCoarseEquivalent(a2));
    204 }
    205 
    206 
    207 TEST(ClustersCoarserPathsTraversal) {
    208   v8::HandleScope scope;
    209   LocalContext env;
    210 
    211   i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
    212 
    213   JSObjectsRetainerTree tree;
    214 
    215   // On the following graph:
    216   //
    217   // p
    218   //   <- o21 <- o11 <-
    219   // q                  o
    220   //   <- o22 <- o12 <-
    221   // r
    222   //
    223   // we expect that coarser will deduce equivalences: p ~ q ~ r,
    224   // o21 ~ o22, and o11 ~ o12.
    225 
    226   JSObjectsCluster o =
    227       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100);
    228   JSObjectsCluster o11 =
    229       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x110, &o);
    230   JSObjectsCluster o12 =
    231       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x120, &o);
    232   JSObjectsCluster o21 =
    233       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x210, &o11);
    234   JSObjectsCluster o22 =
    235       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x220, &o12);
    236   JSObjectsCluster p =
    237       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &o21);
    238   JSObjectsCluster q =
    239       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x310, &o21, &o22);
    240   JSObjectsCluster r =
    241       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x320, &o22);
    242 
    243   ClustersCoarser coarser;
    244   coarser.Process(&tree);
    245 
    246   CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o));
    247   CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o11));
    248   CHECK_EQ(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o12));
    249   CHECK_EQ(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(o22));
    250   CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o21));
    251   CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p));
    252   CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q));
    253   CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r));
    254   CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(p));
    255   CHECK_NE(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(p));
    256 }
    257 
    258 
    259 TEST(ClustersCoarserSelf) {
    260   v8::HandleScope scope;
    261   LocalContext env;
    262 
    263   i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
    264 
    265   JSObjectsRetainerTree tree;
    266 
    267   // On the following graph:
    268   //
    269   // p (self-referencing)
    270   //          <- o1     <-
    271   // q (self-referencing)   o
    272   //          <- o2     <-
    273   // r (self-referencing)
    274   //
    275   // we expect that coarser will deduce equivalences: p ~ q ~ r, o1 ~ o2;
    276 
    277   JSObjectsCluster o =
    278       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100);
    279   JSObjectsCluster o1 =
    280       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x110, &o);
    281   JSObjectsCluster o2 =
    282       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x120, &o);
    283   JSObjectsCluster p =
    284       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &o1);
    285   AddSelfReferenceToTree(&tree, &p);
    286   JSObjectsCluster q =
    287       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x310, &o1, &o2);
    288   AddSelfReferenceToTree(&tree, &q);
    289   JSObjectsCluster r =
    290       AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x320, &o2);
    291   AddSelfReferenceToTree(&tree, &r);
    292 
    293   ClustersCoarser coarser;
    294   coarser.Process(&tree);
    295 
    296   CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o));
    297   CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o1));
    298   CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2));
    299   CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p));
    300   CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q));
    301   CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r));
    302   CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(p));
    303 }
    304 
    305 
    306 namespace {
    307 
    308 class RetainerProfilePrinter : public RetainerHeapProfile::Printer {
    309  public:
    310   RetainerProfilePrinter() : stream_(&allocator_), lines_(100) {}
    311 
    312   void PrintRetainers(const JSObjectsCluster& cluster,
    313                       const i::StringStream& retainers) {
    314     cluster.Print(&stream_);
    315     stream_.Add("%s", *(retainers.ToCString()));
    316     stream_.Put('\0');
    317   }
    318 
    319   const char* GetRetainers(const char* constructor) {
    320     FillLines();
    321     const size_t cons_len = strlen(constructor);
    322     for (int i = 0; i < lines_.length(); ++i) {
    323       if (strncmp(constructor, lines_[i], cons_len) == 0 &&
    324           lines_[i][cons_len] == ',') {
    325         return lines_[i] + cons_len + 1;
    326       }
    327     }
    328     return NULL;
    329   }
    330 
    331  private:
    332   void FillLines() {
    333     if (lines_.length() > 0) return;
    334     stream_.Put('\0');
    335     stream_str_ = stream_.ToCString();
    336     const char* pos = *stream_str_;
    337     while (pos != NULL && *pos != '\0') {
    338       lines_.Add(pos);
    339       pos = strchr(pos, '\0');
    340       if (pos != NULL) ++pos;
    341     }
    342   }
    343 
    344   i::HeapStringAllocator allocator_;
    345   i::StringStream stream_;
    346   i::SmartPointer<const char> stream_str_;
    347   i::List<const char*> lines_;
    348 };
    349 
    350 }  // namespace
    351 
    352 
    353 TEST(RetainerProfile) {
    354   v8::HandleScope scope;
    355   LocalContext env;
    356 
    357   CompileRun(
    358       "function A() {}\n"
    359       "function B(x) { this.x = x; }\n"
    360       "function C(x) { this.x1 = x; this.x2 = x; }\n"
    361       "var a = new A();\n"
    362       "var b1 = new B(a), b2 = new B(a);\n"
    363       "var c = new C(a);");
    364 
    365   RetainerHeapProfile ret_profile;
    366   i::AssertNoAllocation no_alloc;
    367   i::HeapIterator iterator;
    368   for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next())
    369     ret_profile.CollectStats(obj);
    370   ret_profile.CoarseAndAggregate();
    371   RetainerProfilePrinter printer;
    372   ret_profile.DebugPrintStats(&printer);
    373   const char* retainers_of_a = printer.GetRetainers("A");
    374   // The order of retainers is unspecified, so we check string length, and
    375   // verify each retainer separately.
    376   CHECK_EQ(i::StrLength("(global property);1,B;2,C;2"),
    377            i::StrLength(retainers_of_a));
    378   CHECK(strstr(retainers_of_a, "(global property);1") != NULL);
    379   CHECK(strstr(retainers_of_a, "B;2") != NULL);
    380   CHECK(strstr(retainers_of_a, "C;2") != NULL);
    381   CHECK_EQ("(global property);2", printer.GetRetainers("B"));
    382   CHECK_EQ("(global property);1", printer.GetRetainers("C"));
    383 }
    384 
    385 
    386 namespace {
    387 
    388 class NamedEntriesDetector {
    389  public:
    390   NamedEntriesDetector()
    391       : has_A2(false), has_B2(false), has_C2(false) {
    392   }
    393 
    394   void Apply(i::HeapEntry** entry_ptr) {
    395     if (IsReachableNodeWithName(*entry_ptr, "A2")) has_A2 = true;
    396     if (IsReachableNodeWithName(*entry_ptr, "B2")) has_B2 = true;
    397     if (IsReachableNodeWithName(*entry_ptr, "C2")) has_C2 = true;
    398   }
    399 
    400   static bool IsReachableNodeWithName(i::HeapEntry* entry, const char* name) {
    401     return strcmp(name, entry->name()) == 0 && entry->painted_reachable();
    402   }
    403 
    404   bool has_A2;
    405   bool has_B2;
    406   bool has_C2;
    407 };
    408 
    409 }  // namespace
    410 
    411 
    412 static const v8::HeapGraphNode* GetGlobalObject(
    413     const v8::HeapSnapshot* snapshot) {
    414   CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount());
    415   const v8::HeapGraphNode* global_obj =
    416       snapshot->GetRoot()->GetChild(0)->GetToNode();
    417   CHECK_EQ("Object", const_cast<i::HeapEntry*>(
    418       reinterpret_cast<const i::HeapEntry*>(global_obj))->name());
    419   return global_obj;
    420 }
    421 
    422 
    423 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
    424                                             v8::HeapGraphEdge::Type type,
    425                                             const char* name) {
    426   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
    427     const v8::HeapGraphEdge* prop = node->GetChild(i);
    428     v8::String::AsciiValue prop_name(prop->GetName());
    429     if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
    430       return prop->GetToNode();
    431   }
    432   return NULL;
    433 }
    434 
    435 
    436 static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
    437   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
    438     const v8::HeapGraphEdge* prop = node->GetChild(i);
    439     const v8::HeapGraphNode* node = prop->GetToNode();
    440     if (node->GetType() == v8::HeapGraphNode::kString) {
    441       v8::String::AsciiValue node_name(node->GetName());
    442       if (strcmp(contents, *node_name) == 0) return true;
    443     }
    444   }
    445   return false;
    446 }
    447 
    448 
    449 TEST(HeapSnapshot) {
    450   v8::HandleScope scope;
    451   LocalContext env2;
    452 
    453   CompileRun(
    454       "function A2() {}\n"
    455       "function B2(x) { return function() { return typeof x; }; }\n"
    456       "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
    457       "var a2 = new A2();\n"
    458       "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
    459       "var c2 = new C2(a2);");
    460   const v8::HeapSnapshot* snapshot_env2 =
    461       v8::HeapProfiler::TakeSnapshot(v8::String::New("env2"));
    462   i::HeapSnapshot* i_snapshot_env2 =
    463       const_cast<i::HeapSnapshot*>(
    464           reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
    465   const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
    466   // Paint all nodes reachable from global object.
    467   i_snapshot_env2->ClearPaint();
    468   const_cast<i::HeapEntry*>(
    469       reinterpret_cast<const i::HeapEntry*>(global_env2))->PaintAllReachable();
    470 
    471   // Verify, that JS global object of env2 has '..2' properties.
    472   const v8::HeapGraphNode* a2_node =
    473       GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "a2");
    474   CHECK_NE(NULL, a2_node);
    475   CHECK_NE(
    476       NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1"));
    477   CHECK_NE(
    478       NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2"));
    479   CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2"));
    480 
    481   NamedEntriesDetector det;
    482   i_snapshot_env2->IterateEntries(&det);
    483   CHECK(det.has_A2);
    484   CHECK(det.has_B2);
    485   CHECK(det.has_C2);
    486 }
    487 
    488 
    489 TEST(HeapSnapshotObjectSizes) {
    490   v8::HandleScope scope;
    491   LocalContext env;
    492 
    493   //   -a-> X1 --a
    494   // x -b-> X2 <-|
    495   CompileRun(
    496       "function X(a, b) { this.a = a; this.b = b; }\n"
    497       "x = new X(new X(), new X());\n"
    498       "(function() { x.a.a = x.b; })();");
    499   const v8::HeapSnapshot* snapshot =
    500       v8::HeapProfiler::TakeSnapshot(v8::String::New("sizes"));
    501   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    502   const v8::HeapGraphNode* x =
    503       GetProperty(global, v8::HeapGraphEdge::kShortcut, "x");
    504   CHECK_NE(NULL, x);
    505   const v8::HeapGraphNode* x1 =
    506       GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
    507   CHECK_NE(NULL, x1);
    508   const v8::HeapGraphNode* x2 =
    509       GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
    510   CHECK_NE(NULL, x2);
    511 
    512   // Test approximate sizes.
    513   CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(false));
    514   CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(false));
    515   CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(false));
    516   // Test exact sizes.
    517   CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(true));
    518   CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(true));
    519   CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(true));
    520 }
    521 
    522 
    523 TEST(HeapSnapshotEntryChildren) {
    524   v8::HandleScope scope;
    525   LocalContext env;
    526 
    527   CompileRun(
    528       "function A() { }\n"
    529       "a = new A;");
    530   const v8::HeapSnapshot* snapshot =
    531       v8::HeapProfiler::TakeSnapshot(v8::String::New("children"));
    532   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    533   for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
    534     const v8::HeapGraphEdge* prop = global->GetChild(i);
    535     CHECK_EQ(global, prop->GetFromNode());
    536   }
    537   const v8::HeapGraphNode* a =
    538       GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
    539   CHECK_NE(NULL, a);
    540   for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
    541     const v8::HeapGraphEdge* prop = a->GetChild(i);
    542     CHECK_EQ(a, prop->GetFromNode());
    543   }
    544 }
    545 
    546 
    547 TEST(HeapSnapshotCodeObjects) {
    548   v8::HandleScope scope;
    549   LocalContext env;
    550 
    551   CompileRun(
    552       "function lazy(x) { return x - 1; }\n"
    553       "function compiled(x) { return x + 1; }\n"
    554       "var anonymous = (function() { return function() { return 0; } })();\n"
    555       "compiled(1)");
    556   const v8::HeapSnapshot* snapshot =
    557       v8::HeapProfiler::TakeSnapshot(v8::String::New("code"));
    558 
    559   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    560   const v8::HeapGraphNode* compiled =
    561       GetProperty(global, v8::HeapGraphEdge::kShortcut, "compiled");
    562   CHECK_NE(NULL, compiled);
    563   CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
    564   const v8::HeapGraphNode* lazy =
    565       GetProperty(global, v8::HeapGraphEdge::kShortcut, "lazy");
    566   CHECK_NE(NULL, lazy);
    567   CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
    568   const v8::HeapGraphNode* anonymous =
    569       GetProperty(global, v8::HeapGraphEdge::kShortcut, "anonymous");
    570   CHECK_NE(NULL, anonymous);
    571   CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
    572   v8::String::AsciiValue anonymous_name(anonymous->GetName());
    573   CHECK_EQ("", *anonymous_name);
    574 
    575   // Find references to code.
    576   const v8::HeapGraphNode* compiled_code =
    577       GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared");
    578   CHECK_NE(NULL, compiled_code);
    579   const v8::HeapGraphNode* lazy_code =
    580       GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
    581   CHECK_NE(NULL, lazy_code);
    582 
    583   // Verify that non-compiled code doesn't contain references to "x"
    584   // literal, while compiled code does. The scope info is stored in FixedArray
    585   // objects attached to the SharedFunctionInfo.
    586   bool compiled_references_x = false, lazy_references_x = false;
    587   for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
    588     const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
    589     const v8::HeapGraphNode* node = prop->GetToNode();
    590     if (node->GetType() == v8::HeapGraphNode::kArray) {
    591       if (HasString(node, "x")) {
    592         compiled_references_x = true;
    593         break;
    594       }
    595     }
    596   }
    597   for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
    598     const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
    599     const v8::HeapGraphNode* node = prop->GetToNode();
    600     if (node->GetType() == v8::HeapGraphNode::kArray) {
    601       if (HasString(node, "x")) {
    602         lazy_references_x = true;
    603         break;
    604       }
    605     }
    606   }
    607   CHECK(compiled_references_x);
    608   CHECK(!lazy_references_x);
    609 }
    610 
    611 
    612 TEST(HeapSnapshotHeapNumbers) {
    613   v8::HandleScope scope;
    614   LocalContext env;
    615   CompileRun(
    616       "a = 1;    // a is Smi\n"
    617       "b = 2.5;  // b is HeapNumber");
    618   const v8::HeapSnapshot* snapshot =
    619       v8::HeapProfiler::TakeSnapshot(v8::String::New("numbers"));
    620   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    621   CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kShortcut, "a"));
    622   const v8::HeapGraphNode* b =
    623       GetProperty(global, v8::HeapGraphEdge::kShortcut, "b");
    624   CHECK_NE(NULL, b);
    625   CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
    626 }
    627 
    628 
    629 TEST(HeapSnapshotInternalReferences) {
    630   v8::HandleScope scope;
    631   v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
    632   global_template->SetInternalFieldCount(2);
    633   LocalContext env(NULL, global_template);
    634   v8::Handle<v8::Object> global_proxy = env->Global();
    635   v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
    636   CHECK_EQ(2, global->InternalFieldCount());
    637   v8::Local<v8::Object> obj = v8::Object::New();
    638   global->SetInternalField(0, v8_num(17));
    639   global->SetInternalField(1, obj);
    640   const v8::HeapSnapshot* snapshot =
    641       v8::HeapProfiler::TakeSnapshot(v8::String::New("internals"));
    642   const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
    643   // The first reference will not present, because it's a Smi.
    644   CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
    645   // The second reference is to an object.
    646   CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
    647 }
    648 
    649 
    650 // Trying to introduce a check helper for uint64_t causes many
    651 // overloading ambiguities, so it seems easier just to cast
    652 // them to a signed type.
    653 #define CHECK_EQ_UINT64_T(a, b) \
    654   CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b))
    655 #define CHECK_NE_UINT64_T(a, b) \
    656   CHECK((a) != (b))  // NOLINT
    657 
    658 TEST(HeapEntryIdsAndGC) {
    659   v8::HandleScope scope;
    660   LocalContext env;
    661 
    662   CompileRun(
    663       "function A() {}\n"
    664       "function B(x) { this.x = x; }\n"
    665       "var a = new A();\n"
    666       "var b = new B(a);");
    667   const v8::HeapSnapshot* snapshot1 =
    668       v8::HeapProfiler::TakeSnapshot(v8::String::New("s1"));
    669 
    670   HEAP->CollectAllGarbage(true);  // Enforce compaction.
    671 
    672   const v8::HeapSnapshot* snapshot2 =
    673       v8::HeapProfiler::TakeSnapshot(v8::String::New("s2"));
    674 
    675   const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
    676   const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
    677   CHECK_NE_UINT64_T(0, global1->GetId());
    678   CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
    679   const v8::HeapGraphNode* A1 =
    680       GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
    681   CHECK_NE(NULL, A1);
    682   const v8::HeapGraphNode* A2 =
    683       GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
    684   CHECK_NE(NULL, A2);
    685   CHECK_NE_UINT64_T(0, A1->GetId());
    686   CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId());
    687   const v8::HeapGraphNode* B1 =
    688       GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
    689   CHECK_NE(NULL, B1);
    690   const v8::HeapGraphNode* B2 =
    691       GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
    692   CHECK_NE(NULL, B2);
    693   CHECK_NE_UINT64_T(0, B1->GetId());
    694   CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId());
    695   const v8::HeapGraphNode* a1 =
    696       GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
    697   CHECK_NE(NULL, a1);
    698   const v8::HeapGraphNode* a2 =
    699       GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
    700   CHECK_NE(NULL, a2);
    701   CHECK_NE_UINT64_T(0, a1->GetId());
    702   CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
    703   const v8::HeapGraphNode* b1 =
    704       GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
    705   CHECK_NE(NULL, b1);
    706   const v8::HeapGraphNode* b2 =
    707       GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
    708   CHECK_NE(NULL, b2);
    709   CHECK_NE_UINT64_T(0, b1->GetId());
    710   CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId());
    711 }
    712 
    713 
    714 TEST(HeapSnapshotRootPreservedAfterSorting) {
    715   v8::HandleScope scope;
    716   LocalContext env;
    717   const v8::HeapSnapshot* snapshot =
    718       v8::HeapProfiler::TakeSnapshot(v8::String::New("s"));
    719   const v8::HeapGraphNode* root1 = snapshot->GetRoot();
    720   const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
    721       snapshot))->GetSortedEntriesList();
    722   const v8::HeapGraphNode* root2 = snapshot->GetRoot();
    723   CHECK_EQ(root1, root2);
    724 }
    725 
    726 
    727 static const v8::HeapGraphNode* GetChild(
    728     const v8::HeapGraphNode* node,
    729     v8::HeapGraphNode::Type type,
    730     const char* name,
    731     const v8::HeapGraphNode* after = NULL) {
    732   bool ignore_child = after == NULL ? false : true;
    733   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
    734     const v8::HeapGraphEdge* prop = node->GetChild(i);
    735     const v8::HeapGraphNode* child = prop->GetToNode();
    736     v8::String::AsciiValue child_name(child->GetName());
    737     if (!ignore_child
    738         && child->GetType() == type
    739         && strcmp(name, *child_name) == 0)
    740       return child;
    741     if (after != NULL && child == after) ignore_child = false;
    742   }
    743   return NULL;
    744 }
    745 
    746 static bool IsNodeRetainedAs(const v8::HeapGraphNode* node,
    747                              int element) {
    748   for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) {
    749     const v8::HeapGraphEdge* prop = node->GetRetainer(i);
    750     if (prop->GetType() == v8::HeapGraphEdge::kElement
    751         && element == prop->GetName()->Int32Value())
    752       return true;
    753   }
    754   return false;
    755 }
    756 
    757 TEST(AggregatedHeapSnapshot) {
    758   v8::HandleScope scope;
    759   LocalContext env;
    760 
    761   CompileRun(
    762       "function A() {}\n"
    763       "function B(x) { this.x = x; }\n"
    764       "var a = new A();\n"
    765       "var b = new B(a);");
    766   const v8::HeapSnapshot* snapshot =
    767       v8::HeapProfiler::TakeSnapshot(
    768           v8::String::New("agg"), v8::HeapSnapshot::kAggregated);
    769   const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(),
    770                                               v8::HeapGraphNode::kHidden,
    771                                               "STRING_TYPE");
    772   CHECK_NE(NULL, strings);
    773   CHECK_NE(0, strings->GetSelfSize());
    774   CHECK_NE(0, strings->GetInstancesCount());
    775   const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(),
    776                                            v8::HeapGraphNode::kHidden,
    777                                            "MAP_TYPE");
    778   CHECK_NE(NULL, maps);
    779   CHECK_NE(0, maps->GetSelfSize());
    780   CHECK_NE(0, maps->GetInstancesCount());
    781 
    782   const v8::HeapGraphNode* a = GetChild(snapshot->GetRoot(),
    783                                         v8::HeapGraphNode::kObject,
    784                                         "A");
    785   CHECK_NE(NULL, a);
    786   CHECK_NE(0, a->GetSelfSize());
    787   CHECK_EQ(1, a->GetInstancesCount());
    788 
    789   const v8::HeapGraphNode* b = GetChild(snapshot->GetRoot(),
    790                                         v8::HeapGraphNode::kObject,
    791                                         "B");
    792   CHECK_NE(NULL, b);
    793   CHECK_NE(0, b->GetSelfSize());
    794   CHECK_EQ(1, b->GetInstancesCount());
    795 
    796   const v8::HeapGraphNode* glob_prop = GetChild(snapshot->GetRoot(),
    797                                                 v8::HeapGraphNode::kObject,
    798                                                 "(global property)",
    799                                                 b);
    800   CHECK_NE(NULL, glob_prop);
    801   CHECK_EQ(0, glob_prop->GetSelfSize());
    802   CHECK_EQ(0, glob_prop->GetInstancesCount());
    803   CHECK_NE(0, glob_prop->GetChildrenCount());
    804 
    805   const v8::HeapGraphNode* a_from_glob_prop = GetChild(
    806       glob_prop,
    807       v8::HeapGraphNode::kObject,
    808       "A");
    809   CHECK_NE(NULL, a_from_glob_prop);
    810   CHECK_EQ(0, a_from_glob_prop->GetSelfSize());
    811   CHECK_EQ(0, a_from_glob_prop->GetInstancesCount());
    812   CHECK_EQ(0, a_from_glob_prop->GetChildrenCount());  // Retains nothing.
    813   CHECK(IsNodeRetainedAs(a_from_glob_prop, 1));  // (global propery) has 1 ref.
    814 
    815   const v8::HeapGraphNode* b_with_children = GetChild(
    816       snapshot->GetRoot(),
    817       v8::HeapGraphNode::kObject,
    818       "B",
    819       b);
    820   CHECK_NE(NULL, b_with_children);
    821   CHECK_EQ(0, b_with_children->GetSelfSize());
    822   CHECK_EQ(0, b_with_children->GetInstancesCount());
    823   CHECK_NE(0, b_with_children->GetChildrenCount());
    824 
    825   const v8::HeapGraphNode* a_from_b = GetChild(
    826       b_with_children,
    827       v8::HeapGraphNode::kObject,
    828       "A");
    829   CHECK_NE(NULL, a_from_b);
    830   CHECK_EQ(0, a_from_b->GetSelfSize());
    831   CHECK_EQ(0, a_from_b->GetInstancesCount());
    832   CHECK_EQ(0, a_from_b->GetChildrenCount());  // Retains nothing.
    833   CHECK(IsNodeRetainedAs(a_from_b, 1));  // B has 1 ref to A.
    834 }
    835 
    836 
    837 TEST(HeapEntryDominator) {
    838   // The graph looks like this:
    839   //
    840   //                   -> node1
    841   //                  a    |^
    842   //          -> node5     ba
    843   //         a             v|
    844   //   node6           -> node2
    845   //         b        a    |^
    846   //          -> node4     ba
    847   //                  b    v|
    848   //                   -> node3
    849   //
    850   // The dominator for all nodes is node6.
    851 
    852   v8::HandleScope scope;
    853   LocalContext env;
    854 
    855   CompileRun(
    856       "function X(a, b) { this.a = a; this.b = b; }\n"
    857       "node6 = new X(new X(new X()), new X(new X(),new X()));\n"
    858       "(function(){\n"
    859       "node6.a.a.b = node6.b.a;  // node1 -> node2\n"
    860       "node6.b.a.a = node6.a.a;  // node2 -> node1\n"
    861       "node6.b.a.b = node6.b.b;  // node2 -> node3\n"
    862       "node6.b.b.a = node6.b.a;  // node3 -> node2\n"
    863       "})();");
    864 
    865   const v8::HeapSnapshot* snapshot =
    866       v8::HeapProfiler::TakeSnapshot(v8::String::New("dominators"));
    867 
    868   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
    869   CHECK_NE(NULL, global);
    870   const v8::HeapGraphNode* node6 =
    871       GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6");
    872   CHECK_NE(NULL, node6);
    873   const v8::HeapGraphNode* node5 =
    874       GetProperty(node6, v8::HeapGraphEdge::kProperty, "a");
    875   CHECK_NE(NULL, node5);
    876   const v8::HeapGraphNode* node4 =
    877       GetProperty(node6, v8::HeapGraphEdge::kProperty, "b");
    878   CHECK_NE(NULL, node4);
    879   const v8::HeapGraphNode* node3 =
    880       GetProperty(node4, v8::HeapGraphEdge::kProperty, "b");
    881   CHECK_NE(NULL, node3);
    882   const v8::HeapGraphNode* node2 =
    883       GetProperty(node4, v8::HeapGraphEdge::kProperty, "a");
    884   CHECK_NE(NULL, node2);
    885   const v8::HeapGraphNode* node1 =
    886       GetProperty(node5, v8::HeapGraphEdge::kProperty, "a");
    887   CHECK_NE(NULL, node1);
    888 
    889   CHECK_EQ(node6, node1->GetDominatorNode());
    890   CHECK_EQ(node6, node2->GetDominatorNode());
    891   CHECK_EQ(node6, node3->GetDominatorNode());
    892   CHECK_EQ(node6, node4->GetDominatorNode());
    893   CHECK_EQ(node6, node5->GetDominatorNode());
    894 }
    895 
    896 
    897 namespace {
    898 
    899 class TestJSONStream : public v8::OutputStream {
    900  public:
    901   TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
    902   explicit TestJSONStream(int abort_countdown)
    903       : eos_signaled_(0), abort_countdown_(abort_countdown) {}
    904   virtual ~TestJSONStream() {}
    905   virtual void EndOfStream() { ++eos_signaled_; }
    906   virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
    907     if (abort_countdown_ > 0) --abort_countdown_;
    908     if (abort_countdown_ == 0) return kAbort;
    909     CHECK_GT(chars_written, 0);
    910     i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
    911     memcpy(chunk.start(), buffer, chars_written);
    912     return kContinue;
    913   }
    914   void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
    915   int eos_signaled() { return eos_signaled_; }
    916   int size() { return buffer_.size(); }
    917  private:
    918   i::Collector<char> buffer_;
    919   int eos_signaled_;
    920   int abort_countdown_;
    921 };
    922 
    923 class AsciiResource: public v8::String::ExternalAsciiStringResource {
    924  public:
    925   explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
    926     length_ = string.length();
    927   }
    928   virtual const char* data() const { return data_; }
    929   virtual size_t length() const { return length_; }
    930  private:
    931   const char* data_;
    932   size_t length_;
    933 };
    934 
    935 }  // namespace
    936 
    937 TEST(HeapSnapshotJSONSerialization) {
    938   v8::HandleScope scope;
    939   LocalContext env;
    940 
    941 #define STRING_LITERAL_FOR_TEST \
    942   "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
    943   CompileRun(
    944       "function A(s) { this.s = s; }\n"
    945       "function B(x) { this.x = x; }\n"
    946       "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
    947       "var b = new B(a);");
    948   const v8::HeapSnapshot* snapshot =
    949       v8::HeapProfiler::TakeSnapshot(v8::String::New("json"));
    950   TestJSONStream stream;
    951   snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
    952   CHECK_GT(stream.size(), 0);
    953   CHECK_EQ(1, stream.eos_signaled());
    954   i::ScopedVector<char> json(stream.size());
    955   stream.WriteTo(json);
    956 
    957   // Verify that snapshot string is valid JSON.
    958   AsciiResource json_res(json);
    959   v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
    960   env->Global()->Set(v8::String::New("json_snapshot"), json_string);
    961   v8::Local<v8::Value> snapshot_parse_result = CompileRun(
    962       "var parsed = JSON.parse(json_snapshot); true;");
    963   CHECK(!snapshot_parse_result.IsEmpty());
    964 
    965   // Verify that snapshot object has required fields.
    966   v8::Local<v8::Object> parsed_snapshot =
    967       env->Global()->Get(v8::String::New("parsed"))->ToObject();
    968   CHECK(parsed_snapshot->Has(v8::String::New("snapshot")));
    969   CHECK(parsed_snapshot->Has(v8::String::New("nodes")));
    970   CHECK(parsed_snapshot->Has(v8::String::New("strings")));
    971 
    972   // Get node and edge "member" offsets.
    973   v8::Local<v8::Value> meta_analysis_result = CompileRun(
    974       "var parsed_meta = parsed.nodes[0];\n"
    975       "var children_count_offset ="
    976       "    parsed_meta.fields.indexOf('children_count');\n"
    977       "var children_offset ="
    978       "    parsed_meta.fields.indexOf('children');\n"
    979       "var children_meta ="
    980       "    parsed_meta.types[children_offset];\n"
    981       "var child_fields_count = children_meta.fields.length;\n"
    982       "var child_type_offset ="
    983       "    children_meta.fields.indexOf('type');\n"
    984       "var child_name_offset ="
    985       "    children_meta.fields.indexOf('name_or_index');\n"
    986       "var child_to_node_offset ="
    987       "    children_meta.fields.indexOf('to_node');\n"
    988       "var property_type ="
    989       "    children_meta.types[child_type_offset].indexOf('property');\n"
    990       "var shortcut_type ="
    991       "    children_meta.types[child_type_offset].indexOf('shortcut');");
    992   CHECK(!meta_analysis_result.IsEmpty());
    993 
    994   // A helper function for processing encoded nodes.
    995   CompileRun(
    996       "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
    997       "  var nodes = parsed.nodes;\n"
    998       "  var strings = parsed.strings;\n"
    999       "  for (var i = 0,\n"
   1000       "      count = nodes[pos + children_count_offset] * child_fields_count;\n"
   1001       "      i < count; i += child_fields_count) {\n"
   1002       "    var child_pos = pos + children_offset + i;\n"
   1003       "    if (nodes[child_pos + child_type_offset] === prop_type\n"
   1004       "       && strings[nodes[child_pos + child_name_offset]] === prop_name)\n"
   1005       "        return nodes[child_pos + child_to_node_offset];\n"
   1006       "  }\n"
   1007       "  return null;\n"
   1008       "}\n");
   1009   // Get the string index using the path: <root> -> <global>.b.x.s
   1010   v8::Local<v8::Value> string_obj_pos_val = CompileRun(
   1011       "GetChildPosByProperty(\n"
   1012       "  GetChildPosByProperty(\n"
   1013       "    GetChildPosByProperty("
   1014       "      parsed.nodes[1 + children_offset + child_to_node_offset],"
   1015       "      \"b\",shortcut_type),\n"
   1016       "    \"x\", property_type),"
   1017       "  \"s\", property_type)");
   1018   CHECK(!string_obj_pos_val.IsEmpty());
   1019   int string_obj_pos =
   1020       static_cast<int>(string_obj_pos_val->ToNumber()->Value());
   1021   v8::Local<v8::Object> nodes_array =
   1022       parsed_snapshot->Get(v8::String::New("nodes"))->ToObject();
   1023   int string_index = static_cast<int>(
   1024       nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
   1025   CHECK_GT(string_index, 0);
   1026   v8::Local<v8::Object> strings_array =
   1027       parsed_snapshot->Get(v8::String::New("strings"))->ToObject();
   1028   v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
   1029   v8::Local<v8::String> ref_string =
   1030       CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
   1031 #undef STRING_LITERAL_FOR_TEST
   1032   CHECK_EQ(*v8::String::Utf8Value(ref_string),
   1033            *v8::String::Utf8Value(string));
   1034 }
   1035 
   1036 
   1037 TEST(HeapSnapshotJSONSerializationAborting) {
   1038   v8::HandleScope scope;
   1039   LocalContext env;
   1040   const v8::HeapSnapshot* snapshot =
   1041       v8::HeapProfiler::TakeSnapshot(v8::String::New("abort"));
   1042   TestJSONStream stream(5);
   1043   snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
   1044   CHECK_GT(stream.size(), 0);
   1045   CHECK_EQ(0, stream.eos_signaled());
   1046 }
   1047 
   1048 
   1049 // Must not crash in debug mode.
   1050 TEST(AggregatedHeapSnapshotJSONSerialization) {
   1051   v8::HandleScope scope;
   1052   LocalContext env;
   1053 
   1054   const v8::HeapSnapshot* snapshot =
   1055       v8::HeapProfiler::TakeSnapshot(
   1056           v8::String::New("agg"), v8::HeapSnapshot::kAggregated);
   1057   TestJSONStream stream;
   1058   snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
   1059   CHECK_GT(stream.size(), 0);
   1060   CHECK_EQ(1, stream.eos_signaled());
   1061 }
   1062 
   1063 
   1064 TEST(HeapSnapshotGetNodeById) {
   1065   v8::HandleScope scope;
   1066   LocalContext env;
   1067 
   1068   const v8::HeapSnapshot* snapshot =
   1069       v8::HeapProfiler::TakeSnapshot(v8::String::New("id"));
   1070   const v8::HeapGraphNode* root = snapshot->GetRoot();
   1071   CHECK_EQ(root, snapshot->GetNodeById(root->GetId()));
   1072   for (int i = 0, count = root->GetChildrenCount(); i < count; ++i) {
   1073     const v8::HeapGraphEdge* prop = root->GetChild(i);
   1074     CHECK_EQ(
   1075         prop->GetToNode(), snapshot->GetNodeById(prop->GetToNode()->GetId()));
   1076   }
   1077   // Check a big id, which should not exist yet.
   1078   CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL));
   1079 }
   1080 
   1081 
   1082 namespace {
   1083 
   1084 class TestActivityControl : public v8::ActivityControl {
   1085  public:
   1086   explicit TestActivityControl(int abort_count)
   1087       : done_(0), total_(0), abort_count_(abort_count) {}
   1088   ControlOption ReportProgressValue(int done, int total) {
   1089     done_ = done;
   1090     total_ = total;
   1091     return --abort_count_ != 0 ? kContinue : kAbort;
   1092   }
   1093   int done() { return done_; }
   1094   int total() { return total_; }
   1095 
   1096  private:
   1097   int done_;
   1098   int total_;
   1099   int abort_count_;
   1100 };
   1101 }
   1102 
   1103 TEST(TakeHeapSnapshotAborting) {
   1104   v8::HandleScope scope;
   1105   LocalContext env;
   1106 
   1107   const int snapshots_count = v8::HeapProfiler::GetSnapshotsCount();
   1108   TestActivityControl aborting_control(3);
   1109   const v8::HeapSnapshot* no_snapshot =
   1110       v8::HeapProfiler::TakeSnapshot(v8::String::New("abort"),
   1111                                      v8::HeapSnapshot::kFull,
   1112                                      &aborting_control);
   1113   CHECK_EQ(NULL, no_snapshot);
   1114   CHECK_EQ(snapshots_count, v8::HeapProfiler::GetSnapshotsCount());
   1115   CHECK_GT(aborting_control.total(), aborting_control.done());
   1116 
   1117   TestActivityControl control(-1);  // Don't abort.
   1118   const v8::HeapSnapshot* snapshot =
   1119       v8::HeapProfiler::TakeSnapshot(v8::String::New("full"),
   1120                                      v8::HeapSnapshot::kFull,
   1121                                      &control);
   1122   CHECK_NE(NULL, snapshot);
   1123   CHECK_EQ(snapshots_count + 1, v8::HeapProfiler::GetSnapshotsCount());
   1124   CHECK_EQ(control.total(), control.done());
   1125   CHECK_GT(control.total(), 0);
   1126 }
   1127 
   1128 
   1129 namespace {
   1130 
   1131 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
   1132  public:
   1133   TestRetainedObjectInfo(int hash,
   1134                          const char* label,
   1135                          intptr_t element_count = -1,
   1136                          intptr_t size = -1)
   1137       : disposed_(false),
   1138         hash_(hash),
   1139         label_(label),
   1140         element_count_(element_count),
   1141         size_(size) {
   1142     instances.Add(this);
   1143   }
   1144   virtual ~TestRetainedObjectInfo() {}
   1145   virtual void Dispose() {
   1146     CHECK(!disposed_);
   1147     disposed_ = true;
   1148   }
   1149   virtual bool IsEquivalent(RetainedObjectInfo* other) {
   1150     return GetHash() == other->GetHash();
   1151   }
   1152   virtual intptr_t GetHash() { return hash_; }
   1153   virtual const char* GetLabel() { return label_; }
   1154   virtual intptr_t GetElementCount() { return element_count_; }
   1155   virtual intptr_t GetSizeInBytes() { return size_; }
   1156   bool disposed() { return disposed_; }
   1157 
   1158   static v8::RetainedObjectInfo* WrapperInfoCallback(
   1159       uint16_t class_id, v8::Handle<v8::Value> wrapper) {
   1160     if (class_id == 1) {
   1161       if (wrapper->IsString()) {
   1162         v8::String::AsciiValue ascii(wrapper);
   1163         if (strcmp(*ascii, "AAA") == 0)
   1164           return new TestRetainedObjectInfo(1, "aaa", 100);
   1165         else if (strcmp(*ascii, "BBB") == 0)
   1166           return new TestRetainedObjectInfo(1, "aaa", 100);
   1167       }
   1168     } else if (class_id == 2) {
   1169       if (wrapper->IsString()) {
   1170         v8::String::AsciiValue ascii(wrapper);
   1171         if (strcmp(*ascii, "CCC") == 0)
   1172           return new TestRetainedObjectInfo(2, "ccc");
   1173       }
   1174     }
   1175     CHECK(false);
   1176     return NULL;
   1177   }
   1178 
   1179   static i::List<TestRetainedObjectInfo*> instances;
   1180 
   1181  private:
   1182   bool disposed_;
   1183   int category_;
   1184   int hash_;
   1185   const char* label_;
   1186   intptr_t element_count_;
   1187   intptr_t size_;
   1188 };
   1189 
   1190 
   1191 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
   1192 }
   1193 
   1194 
   1195 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
   1196                                         v8::HeapGraphNode::Type type,
   1197                                         const char* name) {
   1198   for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
   1199     const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
   1200     if (node->GetType() == type && strcmp(name,
   1201                const_cast<i::HeapEntry*>(
   1202                    reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
   1203       return node;
   1204     }
   1205   }
   1206   return NULL;
   1207 }
   1208 
   1209 
   1210 TEST(HeapSnapshotRetainedObjectInfo) {
   1211   v8::HandleScope scope;
   1212   LocalContext env;
   1213 
   1214   v8::HeapProfiler::DefineWrapperClass(
   1215       1, TestRetainedObjectInfo::WrapperInfoCallback);
   1216   v8::HeapProfiler::DefineWrapperClass(
   1217       2, TestRetainedObjectInfo::WrapperInfoCallback);
   1218   v8::Persistent<v8::String> p_AAA =
   1219       v8::Persistent<v8::String>::New(v8_str("AAA"));
   1220   p_AAA.SetWrapperClassId(1);
   1221   v8::Persistent<v8::String> p_BBB =
   1222       v8::Persistent<v8::String>::New(v8_str("BBB"));
   1223   p_BBB.SetWrapperClassId(1);
   1224   v8::Persistent<v8::String> p_CCC =
   1225       v8::Persistent<v8::String>::New(v8_str("CCC"));
   1226   p_CCC.SetWrapperClassId(2);
   1227   CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
   1228   const v8::HeapSnapshot* snapshot =
   1229       v8::HeapProfiler::TakeSnapshot(v8::String::New("retained"));
   1230 
   1231   CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
   1232   for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
   1233     CHECK(TestRetainedObjectInfo::instances[i]->disposed());
   1234     delete TestRetainedObjectInfo::instances[i];
   1235   }
   1236 
   1237   const v8::HeapGraphNode* natives = GetNode(
   1238       snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(Native objects)");
   1239   CHECK_NE(NULL, natives);
   1240   CHECK_EQ(2, natives->GetChildrenCount());
   1241   const v8::HeapGraphNode* aaa = GetNode(
   1242       natives, v8::HeapGraphNode::kNative, "aaa / 100 entries");
   1243   CHECK_NE(NULL, aaa);
   1244   const v8::HeapGraphNode* ccc = GetNode(
   1245       natives, v8::HeapGraphNode::kNative, "ccc");
   1246   CHECK_NE(NULL, ccc);
   1247 
   1248   CHECK_EQ(2, aaa->GetChildrenCount());
   1249   const v8::HeapGraphNode* n_AAA = GetNode(
   1250       aaa, v8::HeapGraphNode::kString, "AAA");
   1251   CHECK_NE(NULL, n_AAA);
   1252   const v8::HeapGraphNode* n_BBB = GetNode(
   1253       aaa, v8::HeapGraphNode::kString, "BBB");
   1254   CHECK_NE(NULL, n_BBB);
   1255   CHECK_EQ(1, ccc->GetChildrenCount());
   1256   const v8::HeapGraphNode* n_CCC = GetNode(
   1257       ccc, v8::HeapGraphNode::kString, "CCC");
   1258   CHECK_NE(NULL, n_CCC);
   1259 
   1260   CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
   1261   CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
   1262   CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
   1263 }
   1264 
   1265 
   1266 TEST(DeleteAllHeapSnapshots) {
   1267   v8::HandleScope scope;
   1268   LocalContext env;
   1269 
   1270   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
   1271   v8::HeapProfiler::DeleteAllSnapshots();
   1272   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
   1273   CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8::String::New("1")));
   1274   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
   1275   v8::HeapProfiler::DeleteAllSnapshots();
   1276   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
   1277   CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8::String::New("1")));
   1278   CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8::String::New("2")));
   1279   CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
   1280   v8::HeapProfiler::DeleteAllSnapshots();
   1281   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
   1282 }
   1283 
   1284 
   1285 TEST(DeleteHeapSnapshot) {
   1286   v8::HandleScope scope;
   1287   LocalContext env;
   1288 
   1289   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
   1290   const v8::HeapSnapshot* s1 =
   1291       v8::HeapProfiler::TakeSnapshot(v8::String::New("1"));
   1292   CHECK_NE(NULL, s1);
   1293   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
   1294   unsigned uid1 = s1->GetUid();
   1295   CHECK_EQ(s1, v8::HeapProfiler::FindSnapshot(uid1));
   1296   const_cast<v8::HeapSnapshot*>(s1)->Delete();
   1297   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
   1298   CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid1));
   1299 
   1300   const v8::HeapSnapshot* s2 =
   1301       v8::HeapProfiler::TakeSnapshot(v8::String::New("2"));
   1302   CHECK_NE(NULL, s2);
   1303   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
   1304   unsigned uid2 = s2->GetUid();
   1305   CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
   1306   CHECK_EQ(s2, v8::HeapProfiler::FindSnapshot(uid2));
   1307   const v8::HeapSnapshot* s3 =
   1308       v8::HeapProfiler::TakeSnapshot(v8::String::New("3"));
   1309   CHECK_NE(NULL, s3);
   1310   CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
   1311   unsigned uid3 = s3->GetUid();
   1312   CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
   1313   CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
   1314   const_cast<v8::HeapSnapshot*>(s2)->Delete();
   1315   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
   1316   CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid2));
   1317   CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
   1318   const_cast<v8::HeapSnapshot*>(s3)->Delete();
   1319   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
   1320   CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid3));
   1321 }
   1322 
   1323 #endif  // ENABLE_LOGGING_AND_PROFILING
   1324