Updated V8 from git://github.com/v8/v8.git to 3e6ec7e018bbf2c63ef04b85ff688198ea204c04
[profile/ivi/qtjsbackend.git] / src / 3rdparty / v8 / test / cctest / test-heap-profiler.cc
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 //
3 // Tests for heap profiler
4
5 #include <ctype.h>
6
7 #include "v8.h"
8
9 #include "cctest.h"
10 #include "heap-profiler.h"
11 #include "snapshot.h"
12 #include "debug.h"
13 #include "utils-inl.h"
14 #include "../include/v8-profiler.h"
15
16 namespace {
17
18 class NamedEntriesDetector {
19  public:
20   NamedEntriesDetector()
21       : has_A2(false), has_B2(false), has_C2(false) {
22   }
23
24   void CheckEntry(i::HeapEntry* entry) {
25     if (strcmp(entry->name(), "A2") == 0) has_A2 = true;
26     if (strcmp(entry->name(), "B2") == 0) has_B2 = true;
27     if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
28   }
29
30   void CheckAllReachables(i::HeapEntry* root) {
31     i::List<i::HeapEntry*> list(10);
32     list.Add(root);
33     root->paint();
34     CheckEntry(root);
35     while (!list.is_empty()) {
36       i::HeapEntry* entry = list.RemoveLast();
37       i::Vector<i::HeapGraphEdge*> children = entry->children();
38       for (int i = 0; i < children.length(); ++i) {
39         if (children[i]->type() == i::HeapGraphEdge::kShortcut) continue;
40         i::HeapEntry* child = children[i]->to();
41         if (!child->painted()) {
42           list.Add(child);
43           child->paint();
44           CheckEntry(child);
45         }
46       }
47     }
48   }
49
50   bool has_A2;
51   bool has_B2;
52   bool has_C2;
53 };
54
55 }  // namespace
56
57
58 static const v8::HeapGraphNode* GetGlobalObject(
59     const v8::HeapSnapshot* snapshot) {
60   CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount());
61   const v8::HeapGraphNode* global_obj =
62       snapshot->GetRoot()->GetChild(0)->GetToNode();
63   CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
64       reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
65   return global_obj;
66 }
67
68
69 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
70                                             v8::HeapGraphEdge::Type type,
71                                             const char* name) {
72   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
73     const v8::HeapGraphEdge* prop = node->GetChild(i);
74     v8::String::AsciiValue prop_name(prop->GetName());
75     if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
76       return prop->GetToNode();
77   }
78   return NULL;
79 }
80
81
82 static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
83   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
84     const v8::HeapGraphEdge* prop = node->GetChild(i);
85     const v8::HeapGraphNode* node = prop->GetToNode();
86     if (node->GetType() == v8::HeapGraphNode::kString) {
87       v8::String::AsciiValue node_name(node->GetName());
88       if (strcmp(contents, *node_name) == 0) return true;
89     }
90   }
91   return false;
92 }
93
94
95 TEST(HeapSnapshot) {
96   v8::HandleScope scope;
97   LocalContext env2;
98
99   CompileRun(
100       "function A2() {}\n"
101       "function B2(x) { return function() { return typeof x; }; }\n"
102       "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
103       "var a2 = new A2();\n"
104       "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
105       "var c2 = new C2(a2);");
106   const v8::HeapSnapshot* snapshot_env2 =
107       v8::HeapProfiler::TakeSnapshot(v8_str("env2"));
108   i::HeapSnapshot* i_snapshot_env2 =
109       const_cast<i::HeapSnapshot*>(
110           reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
111   const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
112
113   // Verify, that JS global object of env2 has '..2' properties.
114   const v8::HeapGraphNode* a2_node =
115       GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
116   CHECK_NE(NULL, a2_node);
117   CHECK_NE(
118       NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
119   CHECK_NE(
120       NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
121   CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
122
123   // Paint all nodes reachable from global object.
124   NamedEntriesDetector det;
125   i_snapshot_env2->ClearPaint();
126   det.CheckAllReachables(const_cast<i::HeapEntry*>(
127       reinterpret_cast<const i::HeapEntry*>(global_env2)));
128   CHECK(det.has_A2);
129   CHECK(det.has_B2);
130   CHECK(det.has_C2);
131 }
132
133
134 TEST(HeapSnapshotObjectSizes) {
135   v8::HandleScope scope;
136   LocalContext env;
137
138   //   -a-> X1 --a
139   // x -b-> X2 <-|
140   CompileRun(
141       "function X(a, b) { this.a = a; this.b = b; }\n"
142       "x = new X(new X(), new X());\n"
143       "dummy = new X();\n"
144       "(function() { x.a.a = x.b; })();");
145   const v8::HeapSnapshot* snapshot =
146       v8::HeapProfiler::TakeSnapshot(v8_str("sizes"));
147   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
148   const v8::HeapGraphNode* x =
149       GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
150   CHECK_NE(NULL, x);
151   const v8::HeapGraphNode* x1 =
152       GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
153   CHECK_NE(NULL, x1);
154   const v8::HeapGraphNode* x2 =
155       GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
156   CHECK_NE(NULL, x2);
157
158   // Test sizes.
159   CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize());
160   CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize());
161   CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize());
162 }
163
164
165 TEST(BoundFunctionInSnapshot) {
166   v8::HandleScope scope;
167   LocalContext env;
168   CompileRun(
169       "function myFunction(a, b) { this.a = a; this.b = b; }\n"
170       "function AAAAA() {}\n"
171       "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
172   const v8::HeapSnapshot* snapshot =
173       v8::HeapProfiler::TakeSnapshot(v8_str("sizes"));
174   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
175   const v8::HeapGraphNode* f =
176       GetProperty(global, v8::HeapGraphEdge::kProperty, "boundFunction");
177   CHECK(f);
178   CHECK_EQ(v8::String::New("native_bind"), f->GetName());
179   const v8::HeapGraphNode* bindings =
180       GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings");
181   CHECK_NE(NULL, bindings);
182   CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
183   CHECK_EQ(4, bindings->GetChildrenCount());
184
185   const v8::HeapGraphNode* bound_this = GetProperty(
186       f, v8::HeapGraphEdge::kShortcut, "bound_this");
187   CHECK(bound_this);
188   CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());
189
190   const v8::HeapGraphNode* bound_function = GetProperty(
191       f, v8::HeapGraphEdge::kShortcut, "bound_function");
192   CHECK(bound_function);
193   CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());
194
195   const v8::HeapGraphNode* bound_argument = GetProperty(
196       f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
197   CHECK(bound_argument);
198   CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
199 }
200
201
202 TEST(HeapSnapshotEntryChildren) {
203   v8::HandleScope scope;
204   LocalContext env;
205
206   CompileRun(
207       "function A() { }\n"
208       "a = new A;");
209   const v8::HeapSnapshot* snapshot =
210       v8::HeapProfiler::TakeSnapshot(v8_str("children"));
211   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
212   for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
213     const v8::HeapGraphEdge* prop = global->GetChild(i);
214     CHECK_EQ(global, prop->GetFromNode());
215   }
216   const v8::HeapGraphNode* a =
217       GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
218   CHECK_NE(NULL, a);
219   for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
220     const v8::HeapGraphEdge* prop = a->GetChild(i);
221     CHECK_EQ(a, prop->GetFromNode());
222   }
223 }
224
225
226 TEST(HeapSnapshotCodeObjects) {
227   v8::HandleScope scope;
228   LocalContext env;
229
230   CompileRun(
231       "function lazy(x) { return x - 1; }\n"
232       "function compiled(x) { return x + 1; }\n"
233       "var anonymous = (function() { return function() { return 0; } })();\n"
234       "compiled(1)");
235   const v8::HeapSnapshot* snapshot =
236       v8::HeapProfiler::TakeSnapshot(v8_str("code"));
237
238   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
239   const v8::HeapGraphNode* compiled =
240       GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
241   CHECK_NE(NULL, compiled);
242   CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
243   const v8::HeapGraphNode* lazy =
244       GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
245   CHECK_NE(NULL, lazy);
246   CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
247   const v8::HeapGraphNode* anonymous =
248       GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
249   CHECK_NE(NULL, anonymous);
250   CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
251   v8::String::AsciiValue anonymous_name(anonymous->GetName());
252   CHECK_EQ("", *anonymous_name);
253
254   // Find references to code.
255   const v8::HeapGraphNode* compiled_code =
256       GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared");
257   CHECK_NE(NULL, compiled_code);
258   const v8::HeapGraphNode* lazy_code =
259       GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
260   CHECK_NE(NULL, lazy_code);
261
262   // Verify that non-compiled code doesn't contain references to "x"
263   // literal, while compiled code does. The scope info is stored in FixedArray
264   // objects attached to the SharedFunctionInfo.
265   bool compiled_references_x = false, lazy_references_x = false;
266   for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
267     const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
268     const v8::HeapGraphNode* node = prop->GetToNode();
269     if (node->GetType() == v8::HeapGraphNode::kArray) {
270       if (HasString(node, "x")) {
271         compiled_references_x = true;
272         break;
273       }
274     }
275   }
276   for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
277     const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
278     const v8::HeapGraphNode* node = prop->GetToNode();
279     if (node->GetType() == v8::HeapGraphNode::kArray) {
280       if (HasString(node, "x")) {
281         lazy_references_x = true;
282         break;
283       }
284     }
285   }
286   CHECK(compiled_references_x);
287   CHECK(!lazy_references_x);
288 }
289
290
291 TEST(HeapSnapshotHeapNumbers) {
292   v8::HandleScope scope;
293   LocalContext env;
294   CompileRun(
295       "a = 1;    // a is Smi\n"
296       "b = 2.5;  // b is HeapNumber");
297   const v8::HeapSnapshot* snapshot =
298       v8::HeapProfiler::TakeSnapshot(v8_str("numbers"));
299   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
300   CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a"));
301   const v8::HeapGraphNode* b =
302       GetProperty(global, v8::HeapGraphEdge::kProperty, "b");
303   CHECK_NE(NULL, b);
304   CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
305 }
306
307 TEST(HeapSnapshotSlicedString) {
308   v8::HandleScope scope;
309   LocalContext env;
310   CompileRun(
311       "parent_string = \"123456789.123456789.123456789.123456789.123456789."
312       "123456789.123456789.123456789.123456789.123456789."
313       "123456789.123456789.123456789.123456789.123456789."
314       "123456789.123456789.123456789.123456789.123456789.\";"
315       "child_string = parent_string.slice(100);");
316   const v8::HeapSnapshot* snapshot =
317       v8::HeapProfiler::TakeSnapshot(v8_str("strings"));
318   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
319   const v8::HeapGraphNode* parent_string =
320       GetProperty(global, v8::HeapGraphEdge::kProperty, "parent_string");
321   CHECK_NE(NULL, parent_string);
322   const v8::HeapGraphNode* child_string =
323       GetProperty(global, v8::HeapGraphEdge::kProperty, "child_string");
324   CHECK_NE(NULL, child_string);
325   const v8::HeapGraphNode* parent =
326       GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent");
327   CHECK_EQ(parent_string, parent);
328 }
329
330 TEST(HeapSnapshotInternalReferences) {
331   v8::HandleScope scope;
332   v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
333   global_template->SetInternalFieldCount(2);
334   LocalContext env(NULL, global_template);
335   v8::Handle<v8::Object> global_proxy = env->Global();
336   v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
337   CHECK_EQ(2, global->InternalFieldCount());
338   v8::Local<v8::Object> obj = v8::Object::New();
339   global->SetInternalField(0, v8_num(17));
340   global->SetInternalField(1, obj);
341   const v8::HeapSnapshot* snapshot =
342       v8::HeapProfiler::TakeSnapshot(v8_str("internals"));
343   const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
344   // The first reference will not present, because it's a Smi.
345   CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
346   // The second reference is to an object.
347   CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
348 }
349
350
351 // Trying to introduce a check helper for uint32_t causes many
352 // overloading ambiguities, so it seems easier just to cast
353 // them to a signed type.
354 #define CHECK_EQ_SNAPSHOT_OBJECT_ID(a, b) \
355   CHECK_EQ(static_cast<int32_t>(a), static_cast<int32_t>(b))
356 #define CHECK_NE_SNAPSHOT_OBJECT_ID(a, b) \
357   CHECK((a) != (b))  // NOLINT
358
359 TEST(HeapEntryIdsAndArrayShift) {
360   v8::HandleScope scope;
361   LocalContext env;
362
363   CompileRun(
364       "function AnObject() {\n"
365       "    this.first = 'first';\n"
366       "    this.second = 'second';\n"
367       "}\n"
368       "var a = new Array();\n"
369       "for (var i = 0; i < 10; ++i)\n"
370       "  a.push(new AnObject());\n");
371   const v8::HeapSnapshot* snapshot1 =
372       v8::HeapProfiler::TakeSnapshot(v8_str("s1"));
373
374   CompileRun(
375       "for (var i = 0; i < 1; ++i)\n"
376       "  a.shift();\n");
377
378   HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
379
380   const v8::HeapSnapshot* snapshot2 =
381       v8::HeapProfiler::TakeSnapshot(v8_str("s2"));
382
383   const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
384   const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
385   CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId());
386   CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId());
387
388   const v8::HeapGraphNode* a1 =
389       GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
390   CHECK_NE(NULL, a1);
391   const v8::HeapGraphNode* k1 =
392       GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements");
393   CHECK_NE(NULL, k1);
394   const v8::HeapGraphNode* a2 =
395       GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
396   CHECK_NE(NULL, a2);
397   const v8::HeapGraphNode* k2 =
398       GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements");
399   CHECK_NE(NULL, k2);
400
401   CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId());
402   CHECK_EQ_SNAPSHOT_OBJECT_ID(k1->GetId(), k2->GetId());
403 }
404
405 TEST(HeapEntryIdsAndGC) {
406   v8::HandleScope scope;
407   LocalContext env;
408
409   CompileRun(
410       "function A() {}\n"
411       "function B(x) { this.x = x; }\n"
412       "var a = new A();\n"
413       "var b = new B(a);");
414   v8::Local<v8::String> s1_str = v8_str("s1");
415   v8::Local<v8::String> s2_str = v8_str("s2");
416   const v8::HeapSnapshot* snapshot1 =
417       v8::HeapProfiler::TakeSnapshot(s1_str);
418
419   HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
420
421   const v8::HeapSnapshot* snapshot2 =
422       v8::HeapProfiler::TakeSnapshot(s2_str);
423
424   CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000);
425   CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
426         snapshot2->GetMaxSnapshotJSObjectId());
427
428   const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
429   const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
430   CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId());
431   CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId());
432   const v8::HeapGraphNode* A1 =
433       GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
434   CHECK_NE(NULL, A1);
435   const v8::HeapGraphNode* A2 =
436       GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
437   CHECK_NE(NULL, A2);
438   CHECK_NE_SNAPSHOT_OBJECT_ID(0, A1->GetId());
439   CHECK_EQ_SNAPSHOT_OBJECT_ID(A1->GetId(), A2->GetId());
440   const v8::HeapGraphNode* B1 =
441       GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
442   CHECK_NE(NULL, B1);
443   const v8::HeapGraphNode* B2 =
444       GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
445   CHECK_NE(NULL, B2);
446   CHECK_NE_SNAPSHOT_OBJECT_ID(0, B1->GetId());
447   CHECK_EQ_SNAPSHOT_OBJECT_ID(B1->GetId(), B2->GetId());
448   const v8::HeapGraphNode* a1 =
449       GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
450   CHECK_NE(NULL, a1);
451   const v8::HeapGraphNode* a2 =
452       GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
453   CHECK_NE(NULL, a2);
454   CHECK_NE_SNAPSHOT_OBJECT_ID(0, a1->GetId());
455   CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId());
456   const v8::HeapGraphNode* b1 =
457       GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
458   CHECK_NE(NULL, b1);
459   const v8::HeapGraphNode* b2 =
460       GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
461   CHECK_NE(NULL, b2);
462   CHECK_NE_SNAPSHOT_OBJECT_ID(0, b1->GetId());
463   CHECK_EQ_SNAPSHOT_OBJECT_ID(b1->GetId(), b2->GetId());
464 }
465
466
467 TEST(HeapSnapshotRootPreservedAfterSorting) {
468   v8::HandleScope scope;
469   LocalContext env;
470   const v8::HeapSnapshot* snapshot =
471       v8::HeapProfiler::TakeSnapshot(v8_str("s"));
472   const v8::HeapGraphNode* root1 = snapshot->GetRoot();
473   const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
474       snapshot))->GetSortedEntriesList();
475   const v8::HeapGraphNode* root2 = snapshot->GetRoot();
476   CHECK_EQ(root1, root2);
477 }
478
479
480 TEST(HeapEntryDominator) {
481   // The graph looks like this:
482   //
483   //                   -> node1
484   //                  a    |^
485   //          -> node5     ba
486   //         a             v|
487   //   node6           -> node2
488   //         b        a    |^
489   //          -> node4     ba
490   //                  b    v|
491   //                   -> node3
492   //
493   // The dominator for all nodes is node6.
494
495   v8::HandleScope scope;
496   LocalContext env;
497
498   CompileRun(
499       "function X(a, b) { this.a = a; this.b = b; }\n"
500       "node6 = new X(new X(new X()), new X(new X(),new X()));\n"
501       "(function(){\n"
502       "node6.a.a.b = node6.b.a;  // node1 -> node2\n"
503       "node6.b.a.a = node6.a.a;  // node2 -> node1\n"
504       "node6.b.a.b = node6.b.b;  // node2 -> node3\n"
505       "node6.b.b.a = node6.b.a;  // node3 -> node2\n"
506       "})();");
507
508   const v8::HeapSnapshot* snapshot =
509       v8::HeapProfiler::TakeSnapshot(v8_str("dominators"));
510
511   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
512   CHECK_NE(NULL, global);
513   const v8::HeapGraphNode* node6 =
514       GetProperty(global, v8::HeapGraphEdge::kProperty, "node6");
515   CHECK_NE(NULL, node6);
516   const v8::HeapGraphNode* node5 =
517       GetProperty(node6, v8::HeapGraphEdge::kProperty, "a");
518   CHECK_NE(NULL, node5);
519   const v8::HeapGraphNode* node4 =
520       GetProperty(node6, v8::HeapGraphEdge::kProperty, "b");
521   CHECK_NE(NULL, node4);
522   const v8::HeapGraphNode* node3 =
523       GetProperty(node4, v8::HeapGraphEdge::kProperty, "b");
524   CHECK_NE(NULL, node3);
525   const v8::HeapGraphNode* node2 =
526       GetProperty(node4, v8::HeapGraphEdge::kProperty, "a");
527   CHECK_NE(NULL, node2);
528   const v8::HeapGraphNode* node1 =
529       GetProperty(node5, v8::HeapGraphEdge::kProperty, "a");
530   CHECK_NE(NULL, node1);
531
532   CHECK_EQ(node6, node1->GetDominatorNode());
533   CHECK_EQ(node6, node2->GetDominatorNode());
534   CHECK_EQ(node6, node3->GetDominatorNode());
535   CHECK_EQ(node6, node4->GetDominatorNode());
536   CHECK_EQ(node6, node5->GetDominatorNode());
537 }
538
539
540 namespace {
541
542 class TestJSONStream : public v8::OutputStream {
543  public:
544   TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
545   explicit TestJSONStream(int abort_countdown)
546       : eos_signaled_(0), abort_countdown_(abort_countdown) {}
547   virtual ~TestJSONStream() {}
548   virtual void EndOfStream() { ++eos_signaled_; }
549   virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
550     if (abort_countdown_ > 0) --abort_countdown_;
551     if (abort_countdown_ == 0) return kAbort;
552     CHECK_GT(chars_written, 0);
553     i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
554     memcpy(chunk.start(), buffer, chars_written);
555     return kContinue;
556   }
557   virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
558     ASSERT(false);
559     return kAbort;
560   }
561   void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
562   int eos_signaled() { return eos_signaled_; }
563   int size() { return buffer_.size(); }
564
565  private:
566   i::Collector<char> buffer_;
567   int eos_signaled_;
568   int abort_countdown_;
569 };
570
571 class AsciiResource: public v8::String::ExternalAsciiStringResource {
572  public:
573   explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
574     length_ = string.length();
575   }
576   virtual const char* data() const { return data_; }
577   virtual size_t length() const { return length_; }
578  private:
579   const char* data_;
580   size_t length_;
581 };
582
583 }  // namespace
584
585 TEST(HeapSnapshotJSONSerialization) {
586   v8::HandleScope scope;
587   LocalContext env;
588
589 #define STRING_LITERAL_FOR_TEST \
590   "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
591   CompileRun(
592       "function A(s) { this.s = s; }\n"
593       "function B(x) { this.x = x; }\n"
594       "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
595       "var b = new B(a);");
596   const v8::HeapSnapshot* snapshot =
597       v8::HeapProfiler::TakeSnapshot(v8_str("json"));
598   TestJSONStream stream;
599   snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
600   CHECK_GT(stream.size(), 0);
601   CHECK_EQ(1, stream.eos_signaled());
602   i::ScopedVector<char> json(stream.size());
603   stream.WriteTo(json);
604
605   // Verify that snapshot string is valid JSON.
606   AsciiResource json_res(json);
607   v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
608   env->Global()->Set(v8_str("json_snapshot"), json_string);
609   v8::Local<v8::Value> snapshot_parse_result = CompileRun(
610       "var parsed = JSON.parse(json_snapshot); true;");
611   CHECK(!snapshot_parse_result.IsEmpty());
612
613   // Verify that snapshot object has required fields.
614   v8::Local<v8::Object> parsed_snapshot =
615       env->Global()->Get(v8_str("parsed"))->ToObject();
616   CHECK(parsed_snapshot->Has(v8_str("snapshot")));
617   CHECK(parsed_snapshot->Has(v8_str("nodes")));
618   CHECK(parsed_snapshot->Has(v8_str("edges")));
619   CHECK(parsed_snapshot->Has(v8_str("strings")));
620
621   // Get node and edge "member" offsets.
622   v8::Local<v8::Value> meta_analysis_result = CompileRun(
623       "var meta = parsed.snapshot.meta;\n"
624       "var edges_index_offset = meta.node_fields.indexOf('edges_index');\n"
625       "var node_fields_count = meta.node_fields.length;\n"
626       "var edge_fields_count = meta.edge_fields.length;\n"
627       "var edge_type_offset = meta.edge_fields.indexOf('type');\n"
628       "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
629       "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
630       "var property_type ="
631       "    meta.edge_types[edge_type_offset].indexOf('property');\n"
632       "var shortcut_type ="
633       "    meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
634       "parsed.nodes.concat(0, 0, 0, 0, 0, 0, parsed.edges.length);");
635   CHECK(!meta_analysis_result.IsEmpty());
636
637   // A helper function for processing encoded nodes.
638   CompileRun(
639       "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
640       "  var nodes = parsed.nodes;\n"
641       "  var edges = parsed.edges;\n"
642       "  var strings = parsed.strings;\n"
643       "  for (var i = nodes[pos + edges_index_offset],\n"
644       "      count = nodes[pos + node_fields_count + edges_index_offset];\n"
645       "      i < count; i += edge_fields_count) {\n"
646       "    if (edges[i + edge_type_offset] === prop_type\n"
647       "        && strings[edges[i + edge_name_offset]] === prop_name)\n"
648       "      return edges[i + edge_to_node_offset];\n"
649       "  }\n"
650       "  return null;\n"
651       "}\n");
652   // Get the string index using the path: <root> -> <global>.b.x.s
653   v8::Local<v8::Value> string_obj_pos_val = CompileRun(
654       "GetChildPosByProperty(\n"
655       "  GetChildPosByProperty(\n"
656       "    GetChildPosByProperty("
657       "      parsed.edges[parsed.nodes[edges_index_offset]"
658       "                   + edge_to_node_offset],"
659       "      \"b\", property_type),\n"
660       "    \"x\", property_type),"
661       "  \"s\", property_type)");
662   CHECK(!string_obj_pos_val.IsEmpty());
663   int string_obj_pos =
664       static_cast<int>(string_obj_pos_val->ToNumber()->Value());
665   v8::Local<v8::Object> nodes_array =
666       parsed_snapshot->Get(v8_str("nodes"))->ToObject();
667   int string_index = static_cast<int>(
668       nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
669   CHECK_GT(string_index, 0);
670   v8::Local<v8::Object> strings_array =
671       parsed_snapshot->Get(v8_str("strings"))->ToObject();
672   v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
673   v8::Local<v8::String> ref_string =
674       CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
675 #undef STRING_LITERAL_FOR_TEST
676   CHECK_EQ(*v8::String::Utf8Value(ref_string),
677            *v8::String::Utf8Value(string));
678 }
679
680
681 TEST(HeapSnapshotJSONSerializationAborting) {
682   v8::HandleScope scope;
683   LocalContext env;
684   const v8::HeapSnapshot* snapshot =
685       v8::HeapProfiler::TakeSnapshot(v8_str("abort"));
686   TestJSONStream stream(5);
687   snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
688   CHECK_GT(stream.size(), 0);
689   CHECK_EQ(0, stream.eos_signaled());
690 }
691
692 namespace {
693
694 class TestStatsStream : public v8::OutputStream {
695  public:
696   TestStatsStream()
697     : eos_signaled_(0),
698       updates_written_(0),
699       entries_count_(0),
700       entries_size_(0),
701       intervals_count_(0),
702       first_interval_index_(-1) { }
703   TestStatsStream(const TestStatsStream& stream)
704     : v8::OutputStream(stream),
705       eos_signaled_(stream.eos_signaled_),
706       updates_written_(stream.updates_written_),
707       entries_count_(stream.entries_count_),
708       entries_size_(stream.entries_size_),
709       intervals_count_(stream.intervals_count_),
710       first_interval_index_(stream.first_interval_index_) { }
711   virtual ~TestStatsStream() {}
712   virtual void EndOfStream() { ++eos_signaled_; }
713   virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
714     ASSERT(false);
715     return kAbort;
716   }
717   virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
718                                           int updates_written) {
719     ++intervals_count_;
720     ASSERT(updates_written);
721     updates_written_ += updates_written;
722     entries_count_ = 0;
723     if (first_interval_index_ == -1 && updates_written != 0)
724       first_interval_index_ = buffer[0].index;
725     for (int i = 0; i < updates_written; ++i) {
726       entries_count_ += buffer[i].count;
727       entries_size_ += buffer[i].size;
728     }
729
730     return kContinue;
731   }
732   int eos_signaled() { return eos_signaled_; }
733   int updates_written() { return updates_written_; }
734   uint32_t entries_count() const { return entries_count_; }
735   uint32_t entries_size() const { return entries_size_; }
736   int intervals_count() const { return intervals_count_; }
737   int first_interval_index() const { return first_interval_index_; }
738
739  private:
740   int eos_signaled_;
741   int updates_written_;
742   uint32_t entries_count_;
743   uint32_t entries_size_;
744   int intervals_count_;
745   int first_interval_index_;
746 };
747
748 }  // namespace
749
750 static TestStatsStream GetHeapStatsUpdate() {
751   TestStatsStream stream;
752   v8::HeapProfiler::PushHeapObjectsStats(&stream);
753   CHECK_EQ(1, stream.eos_signaled());
754   return stream;
755 }
756
757
758 TEST(HeapSnapshotObjectsStats) {
759   v8::HandleScope scope;
760   LocalContext env;
761
762   v8::HeapProfiler::StartHeapObjectsTracking();
763   // We have to call GC 5 times. In other case the garbage will be
764   // the reason of flakiness.
765   for (int i = 0; i < 5; ++i) {
766     HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
767   }
768
769   {
770     // Single chunk of data expected in update. Initial data.
771     TestStatsStream stats_update = GetHeapStatsUpdate();
772     CHECK_EQ(1, stats_update.intervals_count());
773     CHECK_EQ(1, stats_update.updates_written());
774     CHECK_LT(0, stats_update.entries_size());
775     CHECK_EQ(0, stats_update.first_interval_index());
776   }
777
778   // No data expected in update because nothing has happened.
779   CHECK_EQ(0, GetHeapStatsUpdate().updates_written());
780   {
781     v8::HandleScope inner_scope_1;
782     v8_str("string1");
783     {
784       // Single chunk of data with one new entry expected in update.
785       TestStatsStream stats_update = GetHeapStatsUpdate();
786       CHECK_EQ(1, stats_update.intervals_count());
787       CHECK_EQ(1, stats_update.updates_written());
788       CHECK_LT(0, stats_update.entries_size());
789       CHECK_EQ(1, stats_update.entries_count());
790       CHECK_EQ(2, stats_update.first_interval_index());
791     }
792
793     // No data expected in update because nothing happened.
794     CHECK_EQ(0, GetHeapStatsUpdate().updates_written());
795
796     {
797       v8::HandleScope inner_scope_2;
798       v8_str("string2");
799
800       uint32_t entries_size;
801       {
802         v8::HandleScope inner_scope_3;
803         v8_str("string3");
804         v8_str("string4");
805
806         {
807           // Single chunk of data with three new entries expected in update.
808           TestStatsStream stats_update = GetHeapStatsUpdate();
809           CHECK_EQ(1, stats_update.intervals_count());
810           CHECK_EQ(1, stats_update.updates_written());
811           CHECK_LT(0, entries_size = stats_update.entries_size());
812           CHECK_EQ(3, stats_update.entries_count());
813           CHECK_EQ(4, stats_update.first_interval_index());
814         }
815       }
816
817       {
818         // Single chunk of data with two left entries expected in update.
819         TestStatsStream stats_update = GetHeapStatsUpdate();
820         CHECK_EQ(1, stats_update.intervals_count());
821         CHECK_EQ(1, stats_update.updates_written());
822         CHECK_GT(entries_size, stats_update.entries_size());
823         CHECK_EQ(1, stats_update.entries_count());
824         // Two strings from forth interval were released.
825         CHECK_EQ(4, stats_update.first_interval_index());
826       }
827     }
828
829     {
830       // Single chunk of data with 0 left entries expected in update.
831       TestStatsStream stats_update = GetHeapStatsUpdate();
832       CHECK_EQ(1, stats_update.intervals_count());
833       CHECK_EQ(1, stats_update.updates_written());
834       CHECK_EQ(0, stats_update.entries_size());
835       CHECK_EQ(0, stats_update.entries_count());
836       // The last string from forth interval was released.
837       CHECK_EQ(4, stats_update.first_interval_index());
838     }
839   }
840   {
841     // Single chunk of data with 0 left entries expected in update.
842     TestStatsStream stats_update = GetHeapStatsUpdate();
843     CHECK_EQ(1, stats_update.intervals_count());
844     CHECK_EQ(1, stats_update.updates_written());
845     CHECK_EQ(0, stats_update.entries_size());
846     CHECK_EQ(0, stats_update.entries_count());
847     // The only string from the second interval was released.
848     CHECK_EQ(2, stats_update.first_interval_index());
849   }
850
851   v8::Local<v8::Array> array = v8::Array::New();
852   CHECK_EQ(0, array->Length());
853   // Force array's buffer allocation.
854   array->Set(2, v8_num(7));
855
856   uint32_t entries_size;
857   {
858     // Single chunk of data with 2 entries expected in update.
859     TestStatsStream stats_update = GetHeapStatsUpdate();
860     CHECK_EQ(1, stats_update.intervals_count());
861     CHECK_EQ(1, stats_update.updates_written());
862     CHECK_LT(0, entries_size = stats_update.entries_size());
863     // They are the array and its buffer.
864     CHECK_EQ(2, stats_update.entries_count());
865     CHECK_EQ(8, stats_update.first_interval_index());
866   }
867
868   for (int i = 0; i < 100; ++i)
869     array->Set(i, v8_num(i));
870
871   {
872     // Single chunk of data with 1 entry expected in update.
873     TestStatsStream stats_update = GetHeapStatsUpdate();
874     CHECK_EQ(1, stats_update.intervals_count());
875     // The first interval was changed because old buffer was collected.
876     // The second interval was changed because new buffer was allocated.
877     CHECK_EQ(2, stats_update.updates_written());
878     CHECK_LT(entries_size, stats_update.entries_size());
879     CHECK_EQ(2, stats_update.entries_count());
880     CHECK_EQ(8, stats_update.first_interval_index());
881   }
882
883   v8::HeapProfiler::StopHeapObjectsTracking();
884 }
885
886
887 static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
888                              const v8::HeapGraphNode* node,
889                              int level, int max_level) {
890   if (level > max_level) return;
891   CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
892   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
893     const v8::HeapGraphEdge* prop = node->GetChild(i);
894     const v8::HeapGraphNode* child =
895         snapshot->GetNodeById(prop->GetToNode()->GetId());
896     CHECK_EQ_SNAPSHOT_OBJECT_ID(prop->GetToNode()->GetId(), child->GetId());
897     CHECK_EQ(prop->GetToNode(), child);
898     CheckChildrenIds(snapshot, child, level + 1, max_level);
899   }
900 }
901
902
903 TEST(HeapSnapshotGetNodeById) {
904   v8::HandleScope scope;
905   LocalContext env;
906
907   const v8::HeapSnapshot* snapshot =
908       v8::HeapProfiler::TakeSnapshot(v8_str("id"));
909   const v8::HeapGraphNode* root = snapshot->GetRoot();
910   CheckChildrenIds(snapshot, root, 0, 3);
911   // Check a big id, which should not exist yet.
912   CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL));
913 }
914
915
916 TEST(HeapSnapshotGetSnapshotObjectId) {
917   v8::HandleScope scope;
918   LocalContext env;
919   CompileRun("globalObject = {};\n");
920   const v8::HeapSnapshot* snapshot =
921       v8::HeapProfiler::TakeSnapshot(v8_str("get_snapshot_object_id"));
922   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
923   const v8::HeapGraphNode* global_object =
924       GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject");
925   CHECK(global_object);
926
927   v8::Local<v8::Value> globalObjectHandle =
928       env->Global()->Get(v8::String::New("globalObject"));
929   CHECK(!globalObjectHandle.IsEmpty());
930   CHECK(globalObjectHandle->IsObject());
931
932   v8::SnapshotObjectId id =
933       v8::HeapProfiler::GetSnapshotObjectId(globalObjectHandle);
934   CHECK_NE(static_cast<int>(v8::HeapProfiler::kUnknownObjectId),
935            id);
936   CHECK_EQ(static_cast<int>(id), global_object->GetId());
937 }
938
939
940 TEST(HeapSnapshotUnknownSnapshotObjectId) {
941   v8::HandleScope scope;
942   LocalContext env;
943   CompileRun("globalObject = {};\n");
944   const v8::HeapSnapshot* snapshot =
945       v8::HeapProfiler::TakeSnapshot(v8_str("unknown_object_id"));
946   const v8::HeapGraphNode* node =
947       snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
948   CHECK_EQ(NULL, node);
949 }
950
951
952 namespace {
953
954 class TestActivityControl : public v8::ActivityControl {
955  public:
956   explicit TestActivityControl(int abort_count)
957       : done_(0), total_(0), abort_count_(abort_count) {}
958   ControlOption ReportProgressValue(int done, int total) {
959     done_ = done;
960     total_ = total;
961     return --abort_count_ != 0 ? kContinue : kAbort;
962   }
963   int done() { return done_; }
964   int total() { return total_; }
965
966  private:
967   int done_;
968   int total_;
969   int abort_count_;
970 };
971 }
972
973 TEST(TakeHeapSnapshotAborting) {
974   v8::HandleScope scope;
975   LocalContext env;
976
977   const int snapshots_count = v8::HeapProfiler::GetSnapshotsCount();
978   TestActivityControl aborting_control(1);
979   const v8::HeapSnapshot* no_snapshot =
980       v8::HeapProfiler::TakeSnapshot(v8_str("abort"),
981                                      v8::HeapSnapshot::kFull,
982                                      &aborting_control);
983   CHECK_EQ(NULL, no_snapshot);
984   CHECK_EQ(snapshots_count, v8::HeapProfiler::GetSnapshotsCount());
985   CHECK_GT(aborting_control.total(), aborting_control.done());
986
987   TestActivityControl control(-1);  // Don't abort.
988   const v8::HeapSnapshot* snapshot =
989       v8::HeapProfiler::TakeSnapshot(v8_str("full"),
990                                      v8::HeapSnapshot::kFull,
991                                      &control);
992   CHECK_NE(NULL, snapshot);
993   CHECK_EQ(snapshots_count + 1, v8::HeapProfiler::GetSnapshotsCount());
994   CHECK_EQ(control.total(), control.done());
995   CHECK_GT(control.total(), 0);
996 }
997
998
999 namespace {
1000
1001 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
1002  public:
1003   TestRetainedObjectInfo(int hash,
1004                          const char* group_label,
1005                          const char* label,
1006                          intptr_t element_count = -1,
1007                          intptr_t size = -1)
1008       : disposed_(false),
1009         hash_(hash),
1010         group_label_(group_label),
1011         label_(label),
1012         element_count_(element_count),
1013         size_(size) {
1014     instances.Add(this);
1015   }
1016   virtual ~TestRetainedObjectInfo() {}
1017   virtual void Dispose() {
1018     CHECK(!disposed_);
1019     disposed_ = true;
1020   }
1021   virtual bool IsEquivalent(RetainedObjectInfo* other) {
1022     return GetHash() == other->GetHash();
1023   }
1024   virtual intptr_t GetHash() { return hash_; }
1025   virtual const char* GetGroupLabel() { return group_label_; }
1026   virtual const char* GetLabel() { return label_; }
1027   virtual intptr_t GetElementCount() { return element_count_; }
1028   virtual intptr_t GetSizeInBytes() { return size_; }
1029   bool disposed() { return disposed_; }
1030
1031   static v8::RetainedObjectInfo* WrapperInfoCallback(
1032       uint16_t class_id, v8::Handle<v8::Value> wrapper) {
1033     if (class_id == 1) {
1034       if (wrapper->IsString()) {
1035         v8::String::AsciiValue ascii(wrapper);
1036         if (strcmp(*ascii, "AAA") == 0)
1037           return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1038         else if (strcmp(*ascii, "BBB") == 0)
1039           return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1040       }
1041     } else if (class_id == 2) {
1042       if (wrapper->IsString()) {
1043         v8::String::AsciiValue ascii(wrapper);
1044         if (strcmp(*ascii, "CCC") == 0)
1045           return new TestRetainedObjectInfo(2, "ccc-group", "ccc");
1046       }
1047     }
1048     CHECK(false);
1049     return NULL;
1050   }
1051
1052   static i::List<TestRetainedObjectInfo*> instances;
1053
1054  private:
1055   bool disposed_;
1056   int category_;
1057   int hash_;
1058   const char* group_label_;
1059   const char* label_;
1060   intptr_t element_count_;
1061   intptr_t size_;
1062 };
1063
1064
1065 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
1066 }
1067
1068
1069 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
1070                                         v8::HeapGraphNode::Type type,
1071                                         const char* name) {
1072   for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
1073     const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
1074     if (node->GetType() == type && strcmp(name,
1075                const_cast<i::HeapEntry*>(
1076                    reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
1077       return node;
1078     }
1079   }
1080   return NULL;
1081 }
1082
1083
1084 TEST(HeapSnapshotRetainedObjectInfo) {
1085   v8::HandleScope scope;
1086   LocalContext env;
1087
1088   v8::HeapProfiler::DefineWrapperClass(
1089       1, TestRetainedObjectInfo::WrapperInfoCallback);
1090   v8::HeapProfiler::DefineWrapperClass(
1091       2, TestRetainedObjectInfo::WrapperInfoCallback);
1092   v8::Persistent<v8::String> p_AAA =
1093       v8::Persistent<v8::String>::New(v8_str("AAA"));
1094   p_AAA.SetWrapperClassId(1);
1095   v8::Persistent<v8::String> p_BBB =
1096       v8::Persistent<v8::String>::New(v8_str("BBB"));
1097   p_BBB.SetWrapperClassId(1);
1098   v8::Persistent<v8::String> p_CCC =
1099       v8::Persistent<v8::String>::New(v8_str("CCC"));
1100   p_CCC.SetWrapperClassId(2);
1101   CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
1102   const v8::HeapSnapshot* snapshot =
1103       v8::HeapProfiler::TakeSnapshot(v8_str("retained"));
1104
1105   CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
1106   for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
1107     CHECK(TestRetainedObjectInfo::instances[i]->disposed());
1108     delete TestRetainedObjectInfo::instances[i];
1109   }
1110
1111   const v8::HeapGraphNode* native_group_aaa = GetNode(
1112       snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
1113   CHECK_NE(NULL, native_group_aaa);
1114   CHECK_EQ(1, native_group_aaa->GetChildrenCount());
1115   const v8::HeapGraphNode* aaa = GetNode(
1116       native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries");
1117   CHECK_NE(NULL, aaa);
1118   CHECK_EQ(2, aaa->GetChildrenCount());
1119
1120   const v8::HeapGraphNode* native_group_ccc = GetNode(
1121       snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
1122   const v8::HeapGraphNode* ccc = GetNode(
1123       native_group_ccc, v8::HeapGraphNode::kNative, "ccc");
1124   CHECK_NE(NULL, ccc);
1125
1126   const v8::HeapGraphNode* n_AAA = GetNode(
1127       aaa, v8::HeapGraphNode::kString, "AAA");
1128   CHECK_NE(NULL, n_AAA);
1129   const v8::HeapGraphNode* n_BBB = GetNode(
1130       aaa, v8::HeapGraphNode::kString, "BBB");
1131   CHECK_NE(NULL, n_BBB);
1132   CHECK_EQ(1, ccc->GetChildrenCount());
1133   const v8::HeapGraphNode* n_CCC = GetNode(
1134       ccc, v8::HeapGraphNode::kString, "CCC");
1135   CHECK_NE(NULL, n_CCC);
1136
1137   CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
1138   CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
1139   CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
1140 }
1141
1142
1143 class GraphWithImplicitRefs {
1144  public:
1145   static const int kObjectsCount = 4;
1146   explicit GraphWithImplicitRefs(LocalContext* env) {
1147     CHECK_EQ(NULL, instance_);
1148     instance_ = this;
1149     for (int i = 0; i < kObjectsCount; i++) {
1150       objects_[i] = v8::Persistent<v8::Object>::New(v8::Object::New());
1151     }
1152     (*env)->Global()->Set(v8_str("root_object"), objects_[0]);
1153   }
1154   ~GraphWithImplicitRefs() {
1155     instance_ = NULL;
1156   }
1157
1158   static void gcPrologue() {
1159     instance_->AddImplicitReferences();
1160   }
1161
1162  private:
1163   void AddImplicitReferences() {
1164     // 0 -> 1
1165     v8::V8::AddImplicitReferences(
1166         v8::Persistent<v8::Object>::Cast(objects_[0]), &objects_[1], 1);
1167     // Adding two more references(note length=2 in params): 1 -> 2, 1 -> 3
1168     v8::V8::AddImplicitReferences(
1169         v8::Persistent<v8::Object>::Cast(objects_[1]), &objects_[2], 2);
1170   }
1171
1172   v8::Persistent<v8::Value> objects_[kObjectsCount];
1173   static GraphWithImplicitRefs* instance_;
1174 };
1175
1176 GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL;
1177
1178
1179 TEST(HeapSnapshotImplicitReferences) {
1180   v8::HandleScope scope;
1181   LocalContext env;
1182
1183   GraphWithImplicitRefs graph(&env);
1184   v8::V8::SetGlobalGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1185
1186   const v8::HeapSnapshot* snapshot =
1187       v8::HeapProfiler::TakeSnapshot(v8_str("implicit_refs"));
1188
1189   const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot);
1190   const v8::HeapGraphNode* obj0 = GetProperty(
1191       global_object, v8::HeapGraphEdge::kProperty, "root_object");
1192   CHECK(obj0);
1193   CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType());
1194   const v8::HeapGraphNode* obj1 = GetProperty(
1195       obj0, v8::HeapGraphEdge::kInternal, "native");
1196   CHECK(obj1);
1197   int implicit_targets_count = 0;
1198   for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) {
1199     const v8::HeapGraphEdge* prop = obj1->GetChild(i);
1200     v8::String::AsciiValue prop_name(prop->GetName());
1201     if (prop->GetType() == v8::HeapGraphEdge::kInternal &&
1202         strcmp("native", *prop_name) == 0) {
1203       ++implicit_targets_count;
1204     }
1205   }
1206   CHECK_EQ(2, implicit_targets_count);
1207   v8::V8::SetGlobalGCPrologueCallback(NULL);
1208 }
1209
1210
1211 TEST(DeleteAllHeapSnapshots) {
1212   v8::HandleScope scope;
1213   LocalContext env;
1214
1215   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1216   v8::HeapProfiler::DeleteAllSnapshots();
1217   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1218   CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1")));
1219   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1220   v8::HeapProfiler::DeleteAllSnapshots();
1221   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1222   CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1")));
1223   CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("2")));
1224   CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
1225   v8::HeapProfiler::DeleteAllSnapshots();
1226   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1227 }
1228
1229
1230 TEST(DeleteHeapSnapshot) {
1231   v8::HandleScope scope;
1232   LocalContext env;
1233
1234   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1235   const v8::HeapSnapshot* s1 =
1236       v8::HeapProfiler::TakeSnapshot(v8_str("1"));
1237   CHECK_NE(NULL, s1);
1238   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1239   unsigned uid1 = s1->GetUid();
1240   CHECK_EQ(s1, v8::HeapProfiler::FindSnapshot(uid1));
1241   const_cast<v8::HeapSnapshot*>(s1)->Delete();
1242   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1243   CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid1));
1244
1245   const v8::HeapSnapshot* s2 =
1246       v8::HeapProfiler::TakeSnapshot(v8_str("2"));
1247   CHECK_NE(NULL, s2);
1248   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1249   unsigned uid2 = s2->GetUid();
1250   CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
1251   CHECK_EQ(s2, v8::HeapProfiler::FindSnapshot(uid2));
1252   const v8::HeapSnapshot* s3 =
1253       v8::HeapProfiler::TakeSnapshot(v8_str("3"));
1254   CHECK_NE(NULL, s3);
1255   CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
1256   unsigned uid3 = s3->GetUid();
1257   CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
1258   CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
1259   const_cast<v8::HeapSnapshot*>(s2)->Delete();
1260   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1261   CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid2));
1262   CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
1263   const_cast<v8::HeapSnapshot*>(s3)->Delete();
1264   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1265   CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid3));
1266 }
1267
1268
1269 TEST(DocumentURL) {
1270   v8::HandleScope scope;
1271   LocalContext env;
1272
1273   CompileRun("document = { URL:\"abcdefgh\" };");
1274
1275   const v8::HeapSnapshot* snapshot =
1276       v8::HeapProfiler::TakeSnapshot(v8_str("document"));
1277   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1278   CHECK_NE(NULL, global);
1279   CHECK_EQ("Object / abcdefgh",
1280            const_cast<i::HeapEntry*>(
1281                reinterpret_cast<const i::HeapEntry*>(global))->name());
1282 }
1283
1284
1285 TEST(DocumentWithException) {
1286   v8::HandleScope scope;
1287   LocalContext env;
1288
1289   CompileRun(
1290       "this.__defineGetter__(\"document\", function() { throw new Error(); })");
1291   const v8::HeapSnapshot* snapshot =
1292       v8::HeapProfiler::TakeSnapshot(v8_str("document"));
1293   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1294   CHECK_NE(NULL, global);
1295   CHECK_EQ("Object",
1296            const_cast<i::HeapEntry*>(
1297                reinterpret_cast<const i::HeapEntry*>(global))->name());
1298 }
1299
1300
1301 TEST(DocumentURLWithException) {
1302   v8::HandleScope scope;
1303   LocalContext env;
1304
1305   CompileRun(
1306       "function URLWithException() {}\n"
1307       "URLWithException.prototype = { get URL() { throw new Error(); } };\n"
1308       "document = { URL: new URLWithException() };");
1309   const v8::HeapSnapshot* snapshot =
1310       v8::HeapProfiler::TakeSnapshot(v8_str("document"));
1311   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1312   CHECK_NE(NULL, global);
1313   CHECK_EQ("Object",
1314            const_cast<i::HeapEntry*>(
1315                reinterpret_cast<const i::HeapEntry*>(global))->name());
1316 }
1317
1318
1319 TEST(NoHandleLeaks) {
1320   v8::HandleScope scope;
1321   LocalContext env;
1322
1323   CompileRun("document = { URL:\"abcdefgh\" };");
1324
1325   v8::Handle<v8::String> name(v8_str("leakz"));
1326   int count_before = i::HandleScope::NumberOfHandles();
1327   v8::HeapProfiler::TakeSnapshot(name);
1328   int count_after = i::HandleScope::NumberOfHandles();
1329   CHECK_EQ(count_before, count_after);
1330 }
1331
1332
1333 TEST(NodesIteration) {
1334   v8::HandleScope scope;
1335   LocalContext env;
1336   const v8::HeapSnapshot* snapshot =
1337       v8::HeapProfiler::TakeSnapshot(v8_str("iteration"));
1338   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1339   CHECK_NE(NULL, global);
1340   // Verify that we can find this object by iteration.
1341   const int nodes_count = snapshot->GetNodesCount();
1342   int count = 0;
1343   for (int i = 0; i < nodes_count; ++i) {
1344     if (snapshot->GetNode(i) == global)
1345       ++count;
1346   }
1347   CHECK_EQ(1, count);
1348 }
1349
1350
1351 TEST(GetHeapValue) {
1352   v8::HandleScope scope;
1353   LocalContext env;
1354
1355   CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };");
1356   const v8::HeapSnapshot* snapshot =
1357       v8::HeapProfiler::TakeSnapshot(v8_str("value"));
1358   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1359   CHECK(global->GetHeapValue()->IsObject());
1360   v8::Local<v8::Object> js_global =
1361       env->Global()->GetPrototype().As<v8::Object>();
1362   CHECK(js_global == global->GetHeapValue());
1363   const v8::HeapGraphNode* obj = GetProperty(
1364       global, v8::HeapGraphEdge::kProperty, "a");
1365   CHECK(obj->GetHeapValue()->IsObject());
1366   v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>();
1367   CHECK(js_obj == obj->GetHeapValue());
1368   const v8::HeapGraphNode* s_prop =
1369       GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
1370   v8::Local<v8::String> js_s_prop =
1371       js_obj->Get(v8_str("s_prop")).As<v8::String>();
1372   CHECK(js_s_prop == s_prop->GetHeapValue());
1373   const v8::HeapGraphNode* n_prop =
1374       GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
1375   v8::Local<v8::Number> js_n_prop =
1376       js_obj->Get(v8_str("n_prop")).As<v8::Number>();
1377   CHECK(js_n_prop == n_prop->GetHeapValue());
1378 }
1379
1380
1381 TEST(GetHeapValueForDeletedObject) {
1382   v8::HandleScope scope;
1383   LocalContext env;
1384
1385   // It is impossible to delete a global property, so we are about to delete a
1386   // property of the "a" object. Also, the "p" object can't be an empty one
1387   // because the empty object is static and isn't actually deleted.
1388   CompileRun("a = { p: { r: {} } };");
1389   const v8::HeapSnapshot* snapshot =
1390       v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
1391   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1392   const v8::HeapGraphNode* obj = GetProperty(
1393       global, v8::HeapGraphEdge::kProperty, "a");
1394   const v8::HeapGraphNode* prop = GetProperty(
1395       obj, v8::HeapGraphEdge::kProperty, "p");
1396   {
1397     // Perform the check inside a nested local scope to avoid creating a
1398     // reference to the object we are deleting.
1399     v8::HandleScope scope;
1400     CHECK(prop->GetHeapValue()->IsObject());
1401   }
1402   CompileRun("delete a.p;");
1403   CHECK(prop->GetHeapValue()->IsUndefined());
1404 }
1405
1406
1407 static int StringCmp(const char* ref, i::String* act) {
1408   i::SmartArrayPointer<char> s_act = act->ToCString();
1409   int result = strcmp(ref, *s_act);
1410   if (result != 0)
1411     fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, *s_act);
1412   return result;
1413 }
1414
1415
1416 TEST(GetConstructorName) {
1417   v8::HandleScope scope;
1418   LocalContext env;
1419
1420   CompileRun(
1421       "function Constructor1() {};\n"
1422       "var obj1 = new Constructor1();\n"
1423       "var Constructor2 = function() {};\n"
1424       "var obj2 = new Constructor2();\n"
1425       "var obj3 = {};\n"
1426       "obj3.constructor = function Constructor3() {};\n"
1427       "var obj4 = {};\n"
1428       "// Slow properties\n"
1429       "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1430       "obj4.constructor = function Constructor4() {};\n"
1431       "var obj5 = {};\n"
1432       "var obj6 = {};\n"
1433       "obj6.constructor = 6;");
1434   v8::Local<v8::Object> js_global =
1435       env->Global()->GetPrototype().As<v8::Object>();
1436   v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>();
1437   i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1);
1438   CHECK_EQ(0, StringCmp(
1439       "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
1440   v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>();
1441   i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2);
1442   CHECK_EQ(0, StringCmp(
1443       "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
1444   v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>();
1445   i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3);
1446   CHECK_EQ(0, StringCmp(
1447       "Constructor3", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
1448   v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>();
1449   i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4);
1450   CHECK_EQ(0, StringCmp(
1451       "Constructor4", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
1452   v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>();
1453   i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5);
1454   CHECK_EQ(0, StringCmp(
1455       "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
1456   v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>();
1457   i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6);
1458   CHECK_EQ(0, StringCmp(
1459       "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
1460 }
1461
1462
1463 TEST(FastCaseGetter) {
1464   v8::HandleScope scope;
1465   LocalContext env;
1466
1467   CompileRun("var obj1 = {};\n"
1468              "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1469              "  return 42;\n"
1470              "});\n"
1471              "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1472              "  return this.value_ = value;\n"
1473              "});\n");
1474   const v8::HeapSnapshot* snapshot =
1475       v8::HeapProfiler::TakeSnapshot(v8_str("fastCaseGetter"));
1476
1477   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1478   CHECK_NE(NULL, global);
1479   const v8::HeapGraphNode* obj1 =
1480       GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1481   CHECK_NE(NULL, obj1);
1482   const v8::HeapGraphNode* getterFunction =
1483       GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get-propWithGetter");
1484   CHECK_NE(NULL, getterFunction);
1485   const v8::HeapGraphNode* setterFunction =
1486       GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set-propWithSetter");
1487   CHECK_NE(NULL, setterFunction);
1488 }
1489
1490
1491 bool HasWeakEdge(const v8::HeapGraphNode* node) {
1492   for (int i = 0; i < node->GetChildrenCount(); ++i) {
1493     const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
1494     if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
1495   }
1496   return false;
1497 }
1498
1499
1500 bool HasWeakGlobalHandle() {
1501   const v8::HeapSnapshot* snapshot =
1502       v8::HeapProfiler::TakeSnapshot(v8_str("weaks"));
1503   const v8::HeapGraphNode* gc_roots = GetNode(
1504       snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
1505   CHECK_NE(NULL, gc_roots);
1506   const v8::HeapGraphNode* global_handles = GetNode(
1507       gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
1508   CHECK_NE(NULL, global_handles);
1509   return HasWeakEdge(global_handles);
1510 }
1511
1512
1513 static void PersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
1514   handle.Dispose();
1515 }
1516
1517
1518 TEST(WeakGlobalHandle) {
1519   v8::HandleScope scope;
1520   LocalContext env;
1521
1522   CHECK(!HasWeakGlobalHandle());
1523
1524   v8::Persistent<v8::Object> handle =
1525       v8::Persistent<v8::Object>::New(v8::Object::New());
1526   handle.MakeWeak(NULL, PersistentHandleCallback);
1527
1528   CHECK(HasWeakGlobalHandle());
1529 }
1530
1531
1532 TEST(WeakGlobalContextRefs) {
1533   v8::HandleScope scope;
1534   LocalContext env;
1535
1536   const v8::HeapSnapshot* snapshot =
1537       v8::HeapProfiler::TakeSnapshot(v8_str("weaks"));
1538   const v8::HeapGraphNode* gc_roots = GetNode(
1539       snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
1540   CHECK_NE(NULL, gc_roots);
1541   const v8::HeapGraphNode* global_handles = GetNode(
1542       gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
1543   CHECK_NE(NULL, global_handles);
1544   const v8::HeapGraphNode* global_context = GetNode(
1545       global_handles, v8::HeapGraphNode::kHidden, "system / GlobalContext");
1546   CHECK_NE(NULL, global_context);
1547   CHECK(HasWeakEdge(global_context));
1548 }
1549
1550
1551 TEST(SfiAndJsFunctionWeakRefs) {
1552   v8::HandleScope scope;
1553   LocalContext env;
1554
1555   CompileRun(
1556       "fun = (function (x) { return function () { return x + 1; } })(1);");
1557   const v8::HeapSnapshot* snapshot =
1558       v8::HeapProfiler::TakeSnapshot(v8_str("fun"));
1559   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1560   CHECK_NE(NULL, global);
1561   const v8::HeapGraphNode* fun =
1562       GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
1563   CHECK(HasWeakEdge(fun));
1564   const v8::HeapGraphNode* shared =
1565       GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
1566   CHECK(HasWeakEdge(shared));
1567 }
1568
1569
1570 TEST(NoDebugObjectInSnapshot) {
1571   v8::HandleScope scope;
1572   LocalContext env;
1573
1574   v8::internal::Isolate::Current()->debug()->Load();
1575   CompileRun("foo = {};");
1576   const v8::HeapSnapshot* snapshot =
1577       v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
1578   const v8::HeapGraphNode* root = snapshot->GetRoot();
1579   int globals_count = 0;
1580   for (int i = 0; i < root->GetChildrenCount(); ++i) {
1581     const v8::HeapGraphEdge* edge = root->GetChild(i);
1582     if (edge->GetType() == v8::HeapGraphEdge::kShortcut) {
1583       ++globals_count;
1584       const v8::HeapGraphNode* global = edge->GetToNode();
1585       const v8::HeapGraphNode* foo =
1586           GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
1587       CHECK_NE(NULL, foo);
1588     }
1589   }
1590   CHECK_EQ(1, globals_count);
1591 }
1592
1593
1594 TEST(PersistentHandleCount) {
1595   v8::HandleScope scope;
1596   LocalContext env;
1597
1598   // V8 also uses global handles internally, so we can't test for an absolute
1599   // number.
1600   int global_handle_count = v8::HeapProfiler::GetPersistentHandleCount();
1601
1602   // Create some persistent handles.
1603   v8::Persistent<v8::String> p_AAA =
1604       v8::Persistent<v8::String>::New(v8_str("AAA"));
1605   CHECK_EQ(global_handle_count + 1,
1606            v8::HeapProfiler::GetPersistentHandleCount());
1607   v8::Persistent<v8::String> p_BBB =
1608       v8::Persistent<v8::String>::New(v8_str("BBB"));
1609   CHECK_EQ(global_handle_count + 2,
1610            v8::HeapProfiler::GetPersistentHandleCount());
1611   v8::Persistent<v8::String> p_CCC =
1612       v8::Persistent<v8::String>::New(v8_str("CCC"));
1613   CHECK_EQ(global_handle_count + 3,
1614            v8::HeapProfiler::GetPersistentHandleCount());
1615
1616   // Dipose the persistent handles in a different order.
1617   p_AAA.Dispose();
1618   CHECK_EQ(global_handle_count + 2,
1619            v8::HeapProfiler::GetPersistentHandleCount());
1620   p_CCC.Dispose();
1621   CHECK_EQ(global_handle_count + 1,
1622            v8::HeapProfiler::GetPersistentHandleCount());
1623   p_BBB.Dispose();
1624   CHECK_EQ(global_handle_count, v8::HeapProfiler::GetPersistentHandleCount());
1625 }
1626
1627
1628 TEST(AllStrongGcRootsHaveNames) {
1629   v8::HandleScope scope;
1630   LocalContext env;
1631
1632   CompileRun("foo = {};");
1633   const v8::HeapSnapshot* snapshot =
1634       v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
1635   const v8::HeapGraphNode* gc_roots = GetNode(
1636       snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
1637   CHECK_NE(NULL, gc_roots);
1638   const v8::HeapGraphNode* strong_roots = GetNode(
1639       gc_roots, v8::HeapGraphNode::kObject, "(Strong roots)");
1640   CHECK_NE(NULL, strong_roots);
1641   for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
1642     const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
1643     CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
1644     v8::String::AsciiValue name(edge->GetName());
1645     CHECK(isalpha(**name));
1646   }
1647 }
1648
1649
1650 TEST(NoRefsToNonEssentialEntries) {
1651   v8::HandleScope scope;
1652   LocalContext env;
1653   CompileRun("global_object = {};\n");
1654   const v8::HeapSnapshot* snapshot =
1655       v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
1656   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1657   const v8::HeapGraphNode* global_object =
1658       GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object");
1659   CHECK_NE(NULL, global_object);
1660   const v8::HeapGraphNode* properties =
1661       GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties");
1662   CHECK_EQ(NULL, properties);
1663   const v8::HeapGraphNode* elements =
1664       GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements");
1665   CHECK_EQ(NULL, elements);
1666 }