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