[presubmit] Enable readability/namespace linter checking.
[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 }  // namespace
1356
1357
1358 TEST(TakeHeapSnapshotAborting) {
1359   LocalContext env;
1360   v8::HandleScope scope(env->GetIsolate());
1361
1362   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1363   const int snapshots_count = heap_profiler->GetSnapshotCount();
1364   TestActivityControl aborting_control(1);
1365   const v8::HeapSnapshot* no_snapshot =
1366       heap_profiler->TakeHeapSnapshot(&aborting_control);
1367   CHECK(!no_snapshot);
1368   CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount());
1369   CHECK_GT(aborting_control.total(), aborting_control.done());
1370
1371   TestActivityControl control(-1);  // Don't abort.
1372   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(&control);
1373   CHECK(ValidateSnapshot(snapshot));
1374
1375   CHECK(snapshot);
1376   CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount());
1377   CHECK_EQ(control.total(), control.done());
1378   CHECK_GT(control.total(), 0);
1379 }
1380
1381
1382 namespace {
1383
1384 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
1385  public:
1386   TestRetainedObjectInfo(int hash,
1387                          const char* group_label,
1388                          const char* label,
1389                          intptr_t element_count = -1,
1390                          intptr_t size = -1)
1391       : disposed_(false),
1392         hash_(hash),
1393         group_label_(group_label),
1394         label_(label),
1395         element_count_(element_count),
1396         size_(size) {
1397     instances.Add(this);
1398   }
1399   virtual ~TestRetainedObjectInfo() {}
1400   virtual void Dispose() {
1401     CHECK(!disposed_);
1402     disposed_ = true;
1403   }
1404   virtual bool IsEquivalent(RetainedObjectInfo* other) {
1405     return GetHash() == other->GetHash();
1406   }
1407   virtual intptr_t GetHash() { return hash_; }
1408   virtual const char* GetGroupLabel() { return group_label_; }
1409   virtual const char* GetLabel() { return label_; }
1410   virtual intptr_t GetElementCount() { return element_count_; }
1411   virtual intptr_t GetSizeInBytes() { return size_; }
1412   bool disposed() { return disposed_; }
1413
1414   static v8::RetainedObjectInfo* WrapperInfoCallback(
1415       uint16_t class_id, v8::Handle<v8::Value> wrapper) {
1416     if (class_id == 1) {
1417       if (wrapper->IsString()) {
1418         v8::String::Utf8Value utf8(wrapper);
1419         if (strcmp(*utf8, "AAA") == 0)
1420           return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1421         else if (strcmp(*utf8, "BBB") == 0)
1422           return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1423       }
1424     } else if (class_id == 2) {
1425       if (wrapper->IsString()) {
1426         v8::String::Utf8Value utf8(wrapper);
1427         if (strcmp(*utf8, "CCC") == 0)
1428           return new TestRetainedObjectInfo(2, "ccc-group", "ccc");
1429       }
1430     }
1431     CHECK(false);
1432     return NULL;
1433   }
1434
1435   static i::List<TestRetainedObjectInfo*> instances;
1436
1437  private:
1438   bool disposed_;
1439   int hash_;
1440   const char* group_label_;
1441   const char* label_;
1442   intptr_t element_count_;
1443   intptr_t size_;
1444 };
1445
1446
1447 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
1448
1449 }  // namespace
1450
1451
1452 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
1453                                         v8::HeapGraphNode::Type type,
1454                                         const char* name) {
1455   for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
1456     const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
1457     if (node->GetType() == type && strcmp(name,
1458                const_cast<i::HeapEntry*>(
1459                    reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
1460       return node;
1461     }
1462   }
1463   return NULL;
1464 }
1465
1466
1467 TEST(HeapSnapshotRetainedObjectInfo) {
1468   LocalContext env;
1469   v8::Isolate* isolate = env->GetIsolate();
1470   v8::HandleScope scope(isolate);
1471   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1472
1473   heap_profiler->SetWrapperClassInfoProvider(
1474       1, TestRetainedObjectInfo::WrapperInfoCallback);
1475   heap_profiler->SetWrapperClassInfoProvider(
1476       2, TestRetainedObjectInfo::WrapperInfoCallback);
1477   v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
1478   p_AAA.SetWrapperClassId(1);
1479   v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
1480   p_BBB.SetWrapperClassId(1);
1481   v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC"));
1482   p_CCC.SetWrapperClassId(2);
1483   CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
1484   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1485   CHECK(ValidateSnapshot(snapshot));
1486
1487   CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
1488   for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
1489     CHECK(TestRetainedObjectInfo::instances[i]->disposed());
1490     delete TestRetainedObjectInfo::instances[i];
1491   }
1492
1493   const v8::HeapGraphNode* native_group_aaa = GetNode(
1494       snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
1495   CHECK(native_group_aaa);
1496   CHECK_EQ(1, native_group_aaa->GetChildrenCount());
1497   const v8::HeapGraphNode* aaa = GetNode(
1498       native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries");
1499   CHECK(aaa);
1500   CHECK_EQ(2, aaa->GetChildrenCount());
1501
1502   const v8::HeapGraphNode* native_group_ccc = GetNode(
1503       snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
1504   const v8::HeapGraphNode* ccc = GetNode(
1505       native_group_ccc, v8::HeapGraphNode::kNative, "ccc");
1506   CHECK(ccc);
1507
1508   const v8::HeapGraphNode* n_AAA = GetNode(
1509       aaa, v8::HeapGraphNode::kString, "AAA");
1510   CHECK(n_AAA);
1511   const v8::HeapGraphNode* n_BBB = GetNode(
1512       aaa, v8::HeapGraphNode::kString, "BBB");
1513   CHECK(n_BBB);
1514   CHECK_EQ(1, ccc->GetChildrenCount());
1515   const v8::HeapGraphNode* n_CCC = GetNode(
1516       ccc, v8::HeapGraphNode::kString, "CCC");
1517   CHECK(n_CCC);
1518
1519   CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
1520   CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
1521   CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
1522 }
1523
1524
1525 class GraphWithImplicitRefs {
1526  public:
1527   static const int kObjectsCount = 4;
1528   explicit GraphWithImplicitRefs(LocalContext* env) {
1529     CHECK(!instance_);
1530     instance_ = this;
1531     isolate_ = (*env)->GetIsolate();
1532     for (int i = 0; i < kObjectsCount; i++) {
1533       objects_[i].Reset(isolate_, v8::Object::New(isolate_));
1534     }
1535     (*env)->Global()->Set(v8_str("root_object"),
1536                           v8::Local<v8::Value>::New(isolate_, objects_[0]));
1537   }
1538   ~GraphWithImplicitRefs() {
1539     instance_ = NULL;
1540   }
1541
1542   static void gcPrologue(v8::GCType type, v8::GCCallbackFlags flags) {
1543     instance_->AddImplicitReferences();
1544   }
1545
1546  private:
1547   void AddImplicitReferences() {
1548     // 0 -> 1
1549     isolate_->SetObjectGroupId(objects_[0],
1550                                v8::UniqueId(1));
1551     isolate_->SetReferenceFromGroup(
1552         v8::UniqueId(1), objects_[1]);
1553     // Adding two more references: 1 -> 2, 1 -> 3
1554     isolate_->SetReference(objects_[1].As<v8::Object>(),
1555                            objects_[2]);
1556     isolate_->SetReference(objects_[1].As<v8::Object>(),
1557                            objects_[3]);
1558   }
1559
1560   v8::Persistent<v8::Value> objects_[kObjectsCount];
1561   static GraphWithImplicitRefs* instance_;
1562   v8::Isolate* isolate_;
1563 };
1564
1565 GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL;
1566
1567
1568 TEST(HeapSnapshotImplicitReferences) {
1569   LocalContext env;
1570   v8::HandleScope scope(env->GetIsolate());
1571   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1572
1573   GraphWithImplicitRefs graph(&env);
1574   v8::V8::AddGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1575
1576   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1577   CHECK(ValidateSnapshot(snapshot));
1578
1579   const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot);
1580   const v8::HeapGraphNode* obj0 = GetProperty(
1581       global_object, v8::HeapGraphEdge::kProperty, "root_object");
1582   CHECK(obj0);
1583   CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType());
1584   const v8::HeapGraphNode* obj1 = GetProperty(
1585       obj0, v8::HeapGraphEdge::kInternal, "native");
1586   CHECK(obj1);
1587   int implicit_targets_count = 0;
1588   for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) {
1589     const v8::HeapGraphEdge* prop = obj1->GetChild(i);
1590     v8::String::Utf8Value prop_name(prop->GetName());
1591     if (prop->GetType() == v8::HeapGraphEdge::kInternal &&
1592         strcmp("native", *prop_name) == 0) {
1593       ++implicit_targets_count;
1594     }
1595   }
1596   CHECK_EQ(2, implicit_targets_count);
1597   v8::V8::RemoveGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1598 }
1599
1600
1601 TEST(DeleteAllHeapSnapshots) {
1602   LocalContext env;
1603   v8::HandleScope scope(env->GetIsolate());
1604   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1605
1606   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1607   heap_profiler->DeleteAllHeapSnapshots();
1608   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1609   CHECK(heap_profiler->TakeHeapSnapshot());
1610   CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1611   heap_profiler->DeleteAllHeapSnapshots();
1612   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1613   CHECK(heap_profiler->TakeHeapSnapshot());
1614   CHECK(heap_profiler->TakeHeapSnapshot());
1615   CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1616   heap_profiler->DeleteAllHeapSnapshots();
1617   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1618 }
1619
1620
1621 static bool FindHeapSnapshot(v8::HeapProfiler* profiler,
1622                              const v8::HeapSnapshot* snapshot) {
1623   int length = profiler->GetSnapshotCount();
1624   for (int i = 0; i < length; i++) {
1625     if (snapshot == profiler->GetHeapSnapshot(i)) return true;
1626   }
1627   return false;
1628 }
1629
1630
1631 TEST(DeleteHeapSnapshot) {
1632   LocalContext env;
1633   v8::HandleScope scope(env->GetIsolate());
1634   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1635
1636   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1637   const v8::HeapSnapshot* s1 = heap_profiler->TakeHeapSnapshot();
1638
1639   CHECK(s1);
1640   CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1641   CHECK(FindHeapSnapshot(heap_profiler, s1));
1642   const_cast<v8::HeapSnapshot*>(s1)->Delete();
1643   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1644   CHECK(!FindHeapSnapshot(heap_profiler, s1));
1645
1646   const v8::HeapSnapshot* s2 = heap_profiler->TakeHeapSnapshot();
1647   CHECK(s2);
1648   CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1649   CHECK(FindHeapSnapshot(heap_profiler, s2));
1650   const v8::HeapSnapshot* s3 = heap_profiler->TakeHeapSnapshot();
1651   CHECK(s3);
1652   CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1653   CHECK_NE(s2, s3);
1654   CHECK(FindHeapSnapshot(heap_profiler, s3));
1655   const_cast<v8::HeapSnapshot*>(s2)->Delete();
1656   CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1657   CHECK(!FindHeapSnapshot(heap_profiler, s2));
1658   CHECK(FindHeapSnapshot(heap_profiler, s3));
1659   const_cast<v8::HeapSnapshot*>(s3)->Delete();
1660   CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1661   CHECK(!FindHeapSnapshot(heap_profiler, s3));
1662 }
1663
1664
1665 class NameResolver : public v8::HeapProfiler::ObjectNameResolver {
1666  public:
1667   virtual const char* GetName(v8::Handle<v8::Object> object) {
1668     return "Global object name";
1669   }
1670 };
1671
1672
1673 TEST(GlobalObjectName) {
1674   LocalContext env;
1675   v8::HandleScope scope(env->GetIsolate());
1676   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1677
1678   CompileRun("document = { URL:\"abcdefgh\" };");
1679
1680   NameResolver name_resolver;
1681   const v8::HeapSnapshot* snapshot =
1682       heap_profiler->TakeHeapSnapshot(NULL, &name_resolver);
1683   CHECK(ValidateSnapshot(snapshot));
1684   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1685   CHECK(global);
1686   CHECK_EQ(0,
1687            strcmp("Object / Global object name",
1688                   const_cast<i::HeapEntry*>(
1689                       reinterpret_cast<const i::HeapEntry*>(global))->name()));
1690 }
1691
1692
1693 TEST(GlobalObjectFields) {
1694   LocalContext env;
1695   v8::HandleScope scope(env->GetIsolate());
1696   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1697   CompileRun("obj = {};");
1698   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1699   CHECK(ValidateSnapshot(snapshot));
1700   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1701   const v8::HeapGraphNode* builtins =
1702       GetProperty(global, v8::HeapGraphEdge::kInternal, "builtins");
1703   CHECK(builtins);
1704   const v8::HeapGraphNode* native_context =
1705       GetProperty(global, v8::HeapGraphEdge::kInternal, "native_context");
1706   CHECK(native_context);
1707   const v8::HeapGraphNode* global_proxy =
1708       GetProperty(global, v8::HeapGraphEdge::kInternal, "global_proxy");
1709   CHECK(global_proxy);
1710 }
1711
1712
1713 TEST(NoHandleLeaks) {
1714   LocalContext env;
1715   v8::HandleScope scope(env->GetIsolate());
1716   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1717
1718   CompileRun("document = { URL:\"abcdefgh\" };");
1719
1720   i::Isolate* isolate = CcTest::i_isolate();
1721   int count_before = i::HandleScope::NumberOfHandles(isolate);
1722   heap_profiler->TakeHeapSnapshot();
1723   int count_after = i::HandleScope::NumberOfHandles(isolate);
1724   CHECK_EQ(count_before, count_after);
1725 }
1726
1727
1728 TEST(NodesIteration) {
1729   LocalContext env;
1730   v8::HandleScope scope(env->GetIsolate());
1731   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1732   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1733   CHECK(ValidateSnapshot(snapshot));
1734   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1735   CHECK(global);
1736   // Verify that we can find this object by iteration.
1737   const int nodes_count = snapshot->GetNodesCount();
1738   int count = 0;
1739   for (int i = 0; i < nodes_count; ++i) {
1740     if (snapshot->GetNode(i) == global)
1741       ++count;
1742   }
1743   CHECK_EQ(1, count);
1744 }
1745
1746
1747 TEST(GetHeapValueForNode) {
1748   LocalContext env;
1749   v8::HandleScope scope(env->GetIsolate());
1750   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1751
1752   CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
1753   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1754   CHECK(ValidateSnapshot(snapshot));
1755   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1756   CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject());
1757   v8::Local<v8::Object> js_global =
1758       env->Global()->GetPrototype().As<v8::Object>();
1759   CHECK(js_global == heap_profiler->FindObjectById(global->GetId()));
1760   const v8::HeapGraphNode* obj = GetProperty(
1761       global, v8::HeapGraphEdge::kProperty, "a");
1762   CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject());
1763   v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>();
1764   CHECK(js_obj == heap_profiler->FindObjectById(obj->GetId()));
1765   const v8::HeapGraphNode* s_prop =
1766       GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
1767   v8::Local<v8::String> js_s_prop =
1768       js_obj->Get(v8_str("s_prop")).As<v8::String>();
1769   CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId()));
1770   const v8::HeapGraphNode* n_prop =
1771       GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
1772   v8::Local<v8::String> js_n_prop =
1773       js_obj->Get(v8_str("n_prop")).As<v8::String>();
1774   CHECK(js_n_prop == heap_profiler->FindObjectById(n_prop->GetId()));
1775 }
1776
1777
1778 TEST(GetHeapValueForDeletedObject) {
1779   LocalContext env;
1780   v8::HandleScope scope(env->GetIsolate());
1781   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1782
1783   // It is impossible to delete a global property, so we are about to delete a
1784   // property of the "a" object. Also, the "p" object can't be an empty one
1785   // because the empty object is static and isn't actually deleted.
1786   CompileRun("a = { p: { r: {} } };");
1787   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1788   CHECK(ValidateSnapshot(snapshot));
1789   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1790   const v8::HeapGraphNode* obj = GetProperty(
1791       global, v8::HeapGraphEdge::kProperty, "a");
1792   const v8::HeapGraphNode* prop = GetProperty(
1793       obj, v8::HeapGraphEdge::kProperty, "p");
1794   {
1795     // Perform the check inside a nested local scope to avoid creating a
1796     // reference to the object we are deleting.
1797     v8::HandleScope scope(env->GetIsolate());
1798     CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject());
1799   }
1800   CompileRun("delete a.p;");
1801   CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty());
1802 }
1803
1804
1805 static int StringCmp(const char* ref, i::String* act) {
1806   v8::base::SmartArrayPointer<char> s_act = act->ToCString();
1807   int result = strcmp(ref, s_act.get());
1808   if (result != 0)
1809     fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get());
1810   return result;
1811 }
1812
1813
1814 TEST(GetConstructorName) {
1815   LocalContext env;
1816   v8::HandleScope scope(env->GetIsolate());
1817
1818   CompileRun(
1819       "function Constructor1() {};\n"
1820       "var obj1 = new Constructor1();\n"
1821       "var Constructor2 = function() {};\n"
1822       "var obj2 = new Constructor2();\n"
1823       "var obj3 = {};\n"
1824       "obj3.constructor = function Constructor3() {};\n"
1825       "var obj4 = {};\n"
1826       "// Slow properties\n"
1827       "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1828       "obj4.constructor = function Constructor4() {};\n"
1829       "var obj5 = {};\n"
1830       "var obj6 = {};\n"
1831       "obj6.constructor = 6;");
1832   v8::Local<v8::Object> js_global =
1833       env->Global()->GetPrototype().As<v8::Object>();
1834   v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>();
1835   i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1);
1836   CHECK_EQ(0, StringCmp(
1837       "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
1838   v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>();
1839   i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2);
1840   CHECK_EQ(0, StringCmp(
1841       "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
1842   v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>();
1843   i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3);
1844   // TODO(verwaest): Restore to Constructor3 once supported by the
1845   // heap-snapshot-generator.
1846   CHECK_EQ(
1847       0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
1848   v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>();
1849   i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4);
1850   // TODO(verwaest): Restore to Constructor4 once supported by the
1851   // heap-snapshot-generator.
1852   CHECK_EQ(
1853       0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
1854   v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>();
1855   i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5);
1856   CHECK_EQ(0, StringCmp(
1857       "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
1858   v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>();
1859   i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6);
1860   CHECK_EQ(0, StringCmp(
1861       "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
1862 }
1863
1864
1865 TEST(FastCaseAccessors) {
1866   LocalContext env;
1867   v8::HandleScope scope(env->GetIsolate());
1868   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1869
1870   CompileRun("var obj1 = {};\n"
1871              "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1872              "  return 42;\n"
1873              "});\n"
1874              "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1875              "  return this.value_ = value;\n"
1876              "});\n");
1877   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1878   CHECK(ValidateSnapshot(snapshot));
1879
1880   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1881   CHECK(global);
1882   const v8::HeapGraphNode* obj1 =
1883       GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1884   CHECK(obj1);
1885   const v8::HeapGraphNode* func;
1886   func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
1887   CHECK(func);
1888   func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
1889   CHECK(!func);
1890   func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
1891   CHECK(func);
1892   func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
1893   CHECK(!func);
1894 }
1895
1896
1897 TEST(FastCaseRedefinedAccessors) {
1898   LocalContext env;
1899   v8::HandleScope scope(env->GetIsolate());
1900   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1901
1902   CompileRun(
1903       "var obj1 = {};\n"
1904       "Object.defineProperty(obj1, 'prop', { "
1905       "  get: function() { return 42; },\n"
1906       "  set: function(value) { return this.prop_ = value; },\n"
1907       "  configurable: true,\n"
1908       "  enumerable: true,\n"
1909       "});\n"
1910       "Object.defineProperty(obj1, 'prop', { "
1911       "  get: function() { return 153; },\n"
1912       "  set: function(value) { return this.prop_ = value; },\n"
1913       "  configurable: true,\n"
1914       "  enumerable: true,\n"
1915       "});\n");
1916   v8::Local<v8::Object> js_global =
1917       env->Global()->GetPrototype().As<v8::Object>();
1918   i::Handle<i::JSObject> js_obj1 =
1919       v8::Utils::OpenHandle(*js_global->Get(v8_str("obj1")).As<v8::Object>());
1920   USE(js_obj1);
1921
1922   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1923   CHECK(ValidateSnapshot(snapshot));
1924   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1925   CHECK(global);
1926   const v8::HeapGraphNode* obj1 =
1927       GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1928   CHECK(obj1);
1929   const v8::HeapGraphNode* func;
1930   func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get prop");
1931   CHECK(func);
1932   func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set prop");
1933   CHECK(func);
1934 }
1935
1936
1937 TEST(SlowCaseAccessors) {
1938   LocalContext env;
1939   v8::HandleScope scope(env->GetIsolate());
1940   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1941
1942   CompileRun("var obj1 = {};\n"
1943              "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};"
1944              "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1945              "  return 42;\n"
1946              "});\n"
1947              "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1948              "  return this.value_ = value;\n"
1949              "});\n");
1950   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1951   CHECK(ValidateSnapshot(snapshot));
1952
1953   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1954   CHECK(global);
1955   const v8::HeapGraphNode* obj1 =
1956       GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1957   CHECK(obj1);
1958   const v8::HeapGraphNode* func;
1959   func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
1960   CHECK(func);
1961   func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
1962   CHECK(!func);
1963   func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
1964   CHECK(func);
1965   func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
1966   CHECK(!func);
1967 }
1968
1969
1970 TEST(HiddenPropertiesFastCase) {
1971   v8::Isolate* isolate = CcTest::isolate();
1972   LocalContext env;
1973   v8::HandleScope scope(isolate);
1974   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1975
1976   CompileRun(
1977       "function C(x) { this.a = this; this.b = x; }\n"
1978       "c = new C(2012);\n");
1979   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1980   CHECK(ValidateSnapshot(snapshot));
1981   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1982   const v8::HeapGraphNode* c =
1983       GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
1984   CHECK(c);
1985   const v8::HeapGraphNode* hidden_props =
1986       GetProperty(c, v8::HeapGraphEdge::kInternal, "hidden_properties");
1987   CHECK(!hidden_props);
1988
1989   v8::Handle<v8::Value> cHandle =
1990       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "c"));
1991   CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
1992   cHandle->ToObject(isolate)->SetHiddenValue(v8_str("key"), v8_str("val"));
1993
1994   snapshot = heap_profiler->TakeHeapSnapshot();
1995   CHECK(ValidateSnapshot(snapshot));
1996   global = GetGlobalObject(snapshot);
1997   c = GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
1998   CHECK(c);
1999   hidden_props = GetProperty(c, v8::HeapGraphEdge::kInternal,
2000       "hidden_properties");
2001   CHECK(hidden_props);
2002 }
2003
2004
2005 TEST(AccessorInfo) {
2006   LocalContext env;
2007   v8::HandleScope scope(env->GetIsolate());
2008   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2009
2010   CompileRun("function foo(x) { }\n");
2011   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2012   CHECK(ValidateSnapshot(snapshot));
2013   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2014   const v8::HeapGraphNode* foo =
2015       GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2016   CHECK(foo);
2017   const v8::HeapGraphNode* map =
2018       GetProperty(foo, v8::HeapGraphEdge::kInternal, "map");
2019   CHECK(map);
2020   const v8::HeapGraphNode* descriptors =
2021       GetProperty(map, v8::HeapGraphEdge::kInternal, "descriptors");
2022   CHECK(descriptors);
2023   const v8::HeapGraphNode* length_name =
2024       GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "2");
2025   CHECK(length_name);
2026   CHECK_EQ(0, strcmp("length", *v8::String::Utf8Value(length_name->GetName())));
2027   const v8::HeapGraphNode* length_accessor =
2028       GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "4");
2029   CHECK(length_accessor);
2030   CHECK_EQ(0, strcmp("system / ExecutableAccessorInfo",
2031                      *v8::String::Utf8Value(length_accessor->GetName())));
2032   const v8::HeapGraphNode* name =
2033       GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "name");
2034   CHECK(name);
2035   const v8::HeapGraphNode* getter =
2036       GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "getter");
2037   CHECK(getter);
2038   const v8::HeapGraphNode* setter =
2039       GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "setter");
2040   CHECK(setter);
2041 }
2042
2043
2044 bool HasWeakEdge(const v8::HeapGraphNode* node) {
2045   for (int i = 0; i < node->GetChildrenCount(); ++i) {
2046     const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
2047     if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
2048   }
2049   return false;
2050 }
2051
2052
2053 bool HasWeakGlobalHandle() {
2054   v8::Isolate* isolate = CcTest::isolate();
2055   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2056   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2057   CHECK(ValidateSnapshot(snapshot));
2058   const v8::HeapGraphNode* gc_roots = GetNode(
2059       snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2060   CHECK(gc_roots);
2061   const v8::HeapGraphNode* global_handles = GetNode(
2062       gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
2063   CHECK(global_handles);
2064   return HasWeakEdge(global_handles);
2065 }
2066
2067
2068 static void PersistentHandleCallback(
2069     const v8::WeakCallbackInfo<v8::Persistent<v8::Object> >& data) {
2070   data.GetParameter()->Reset();
2071 }
2072
2073
2074 TEST(WeakGlobalHandle) {
2075   LocalContext env;
2076   v8::HandleScope scope(env->GetIsolate());
2077
2078   CHECK(!HasWeakGlobalHandle());
2079
2080   v8::Persistent<v8::Object> handle(env->GetIsolate(),
2081                                     v8::Object::New(env->GetIsolate()));
2082   handle.SetWeak(&handle, PersistentHandleCallback,
2083                  v8::WeakCallbackType::kParameter);
2084
2085   CHECK(HasWeakGlobalHandle());
2086 }
2087
2088
2089 TEST(SfiAndJsFunctionWeakRefs) {
2090   LocalContext env;
2091   v8::HandleScope scope(env->GetIsolate());
2092   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2093
2094   CompileRun(
2095       "fun = (function (x) { return function () { return x + 1; } })(1);");
2096   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2097   CHECK(ValidateSnapshot(snapshot));
2098   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2099   CHECK(global);
2100   const v8::HeapGraphNode* fun =
2101       GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
2102   CHECK(!HasWeakEdge(fun));
2103   const v8::HeapGraphNode* shared =
2104       GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
2105   CHECK(!HasWeakEdge(shared));
2106 }
2107
2108
2109 TEST(NoDebugObjectInSnapshot) {
2110   LocalContext env;
2111   v8::HandleScope scope(env->GetIsolate());
2112   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2113
2114   CHECK(CcTest::i_isolate()->debug()->Load());
2115   CompileRun("foo = {};");
2116   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2117   CHECK(ValidateSnapshot(snapshot));
2118   const v8::HeapGraphNode* root = snapshot->GetRoot();
2119   int globals_count = 0;
2120   bool found = false;
2121   for (int i = 0; i < root->GetChildrenCount(); ++i) {
2122     const v8::HeapGraphEdge* edge = root->GetChild(i);
2123     if (edge->GetType() == v8::HeapGraphEdge::kShortcut) {
2124       ++globals_count;
2125       const v8::HeapGraphNode* global = edge->GetToNode();
2126       const v8::HeapGraphNode* foo =
2127           GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2128       if (foo != nullptr) {
2129         found = true;
2130       }
2131     }
2132   }
2133   CHECK_EQ(2, globals_count);
2134   CHECK(found);
2135 }
2136
2137
2138 TEST(AllStrongGcRootsHaveNames) {
2139   LocalContext env;
2140   v8::HandleScope scope(env->GetIsolate());
2141   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2142
2143   CompileRun("foo = {};");
2144   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2145   CHECK(ValidateSnapshot(snapshot));
2146   const v8::HeapGraphNode* gc_roots = GetNode(
2147       snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2148   CHECK(gc_roots);
2149   const v8::HeapGraphNode* strong_roots = GetNode(
2150       gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
2151   CHECK(strong_roots);
2152   for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
2153     const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
2154     CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
2155     v8::String::Utf8Value name(edge->GetName());
2156     CHECK(isalpha(**name));
2157   }
2158 }
2159
2160
2161 TEST(NoRefsToNonEssentialEntries) {
2162   LocalContext env;
2163   v8::HandleScope scope(env->GetIsolate());
2164   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2165   CompileRun("global_object = {};\n");
2166   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2167   CHECK(ValidateSnapshot(snapshot));
2168   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2169   const v8::HeapGraphNode* global_object =
2170       GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object");
2171   CHECK(global_object);
2172   const v8::HeapGraphNode* properties =
2173       GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties");
2174   CHECK(!properties);
2175   const v8::HeapGraphNode* elements =
2176       GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements");
2177   CHECK(!elements);
2178 }
2179
2180
2181 TEST(MapHasDescriptorsAndTransitions) {
2182   LocalContext env;
2183   v8::HandleScope scope(env->GetIsolate());
2184   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2185   CompileRun("obj = { a: 10 };\n");
2186   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2187   CHECK(ValidateSnapshot(snapshot));
2188   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2189   const v8::HeapGraphNode* global_object =
2190       GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
2191   CHECK(global_object);
2192
2193   const v8::HeapGraphNode* map =
2194       GetProperty(global_object, v8::HeapGraphEdge::kInternal, "map");
2195   CHECK(map);
2196   const v8::HeapGraphNode* own_descriptors = GetProperty(
2197       map, v8::HeapGraphEdge::kInternal, "descriptors");
2198   CHECK(own_descriptors);
2199   const v8::HeapGraphNode* own_transitions = GetProperty(
2200       map, v8::HeapGraphEdge::kInternal, "transitions");
2201   CHECK(!own_transitions);
2202 }
2203
2204
2205 TEST(ManyLocalsInSharedContext) {
2206   LocalContext env;
2207   v8::HandleScope scope(env->GetIsolate());
2208   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2209   int num_objects = 6000;
2210   CompileRun(
2211       "var n = 6000;"
2212       "var result = [];"
2213       "result.push('(function outer() {');"
2214       "for (var i = 0; i < n; i++) {"
2215       "    var f = 'function f_' + i + '() { ';"
2216       "    if (i > 0)"
2217       "        f += 'f_' + (i - 1) + '();';"
2218       "    f += ' }';"
2219       "    result.push(f);"
2220       "}"
2221       "result.push('return f_' + (n - 1) + ';');"
2222       "result.push('})()');"
2223       "var ok = eval(result.join('\\n'));");
2224   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2225   CHECK(ValidateSnapshot(snapshot));
2226
2227   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2228   CHECK(global);
2229   const v8::HeapGraphNode* ok_object =
2230       GetProperty(global, v8::HeapGraphEdge::kProperty, "ok");
2231   CHECK(ok_object);
2232   const v8::HeapGraphNode* context_object =
2233       GetProperty(ok_object, v8::HeapGraphEdge::kInternal, "context");
2234   CHECK(context_object);
2235   // Check the objects are not duplicated in the context.
2236   CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1,
2237            context_object->GetChildrenCount());
2238   // Check all the objects have got their names.
2239   // ... well check just every 15th because otherwise it's too slow in debug.
2240   for (int i = 0; i < num_objects - 1; i += 15) {
2241     i::EmbeddedVector<char, 100> var_name;
2242     i::SNPrintF(var_name, "f_%d", i);
2243     const v8::HeapGraphNode* f_object = GetProperty(
2244         context_object, v8::HeapGraphEdge::kContextVariable, var_name.start());
2245     CHECK(f_object);
2246   }
2247 }
2248
2249
2250 TEST(AllocationSitesAreVisible) {
2251   LocalContext env;
2252   v8::Isolate* isolate = env->GetIsolate();
2253   v8::HandleScope scope(isolate);
2254   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2255   CompileRun(
2256       "fun = function () { var a = [3, 2, 1]; return a; }\n"
2257       "fun();");
2258   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2259   CHECK(ValidateSnapshot(snapshot));
2260
2261   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2262   CHECK(global);
2263   const v8::HeapGraphNode* fun_code =
2264       GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
2265   CHECK(fun_code);
2266   const v8::HeapGraphNode* literals =
2267       GetProperty(fun_code, v8::HeapGraphEdge::kInternal, "literals");
2268   CHECK(literals);
2269   CHECK_EQ(v8::HeapGraphNode::kArray, literals->GetType());
2270   CHECK_EQ(1, literals->GetChildrenCount());
2271
2272   // The first value in the literals array should be the boilerplate,
2273   // after an AllocationSite.
2274   const v8::HeapGraphEdge* prop = literals->GetChild(0);
2275   const v8::HeapGraphNode* allocation_site = prop->GetToNode();
2276   v8::String::Utf8Value name(allocation_site->GetName());
2277   CHECK_EQ(0, strcmp("system / AllocationSite", *name));
2278   const v8::HeapGraphNode* transition_info =
2279       GetProperty(allocation_site, v8::HeapGraphEdge::kInternal,
2280                   "transition_info");
2281   CHECK(transition_info);
2282
2283   const v8::HeapGraphNode* elements =
2284       GetProperty(transition_info, v8::HeapGraphEdge::kInternal,
2285                   "elements");
2286   CHECK(elements);
2287   CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType());
2288   CHECK_EQ(v8::internal::FixedArray::SizeFor(3),
2289            static_cast<int>(elements->GetShallowSize()));
2290
2291   v8::Handle<v8::Value> array_val =
2292       heap_profiler->FindObjectById(transition_info->GetId());
2293   CHECK(array_val->IsArray());
2294   v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(array_val);
2295   // Verify the array is "a" in the code above.
2296   CHECK_EQ(3u, array->Length());
2297   CHECK(v8::Integer::New(isolate, 3)
2298             ->Equals(array->Get(v8::Integer::New(isolate, 0))));
2299   CHECK(v8::Integer::New(isolate, 2)
2300             ->Equals(array->Get(v8::Integer::New(isolate, 1))));
2301   CHECK(v8::Integer::New(isolate, 1)
2302             ->Equals(array->Get(v8::Integer::New(isolate, 2))));
2303 }
2304
2305
2306 TEST(JSFunctionHasCodeLink) {
2307   LocalContext env;
2308   v8::HandleScope scope(env->GetIsolate());
2309   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2310   CompileRun("function foo(x, y) { return x + y; }\n");
2311   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2312   CHECK(ValidateSnapshot(snapshot));
2313   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2314   const v8::HeapGraphNode* foo_func =
2315       GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2316   CHECK(foo_func);
2317   const v8::HeapGraphNode* code =
2318       GetProperty(foo_func, v8::HeapGraphEdge::kInternal, "code");
2319   CHECK(code);
2320 }
2321
2322
2323 static const v8::HeapGraphNode* GetNodeByPath(const v8::HeapSnapshot* snapshot,
2324                                               const char* path[],
2325                                               int depth) {
2326   const v8::HeapGraphNode* node = snapshot->GetRoot();
2327   for (int current_depth = 0; current_depth < depth; ++current_depth) {
2328     int i, count = node->GetChildrenCount();
2329     for (i = 0; i < count; ++i) {
2330       const v8::HeapGraphEdge* edge = node->GetChild(i);
2331       const v8::HeapGraphNode* to_node = edge->GetToNode();
2332       v8::String::Utf8Value edge_name(edge->GetName());
2333       v8::String::Utf8Value node_name(to_node->GetName());
2334       i::EmbeddedVector<char, 100> name;
2335       i::SNPrintF(name, "%s::%s", *edge_name, *node_name);
2336       if (strstr(name.start(), path[current_depth])) {
2337         node = to_node;
2338         break;
2339       }
2340     }
2341     if (i == count) return NULL;
2342   }
2343   return node;
2344 }
2345
2346
2347 TEST(CheckCodeNames) {
2348   LocalContext env;
2349   v8::HandleScope scope(env->GetIsolate());
2350   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2351   CompileRun("var a = 1.1;");
2352   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2353   CHECK(ValidateSnapshot(snapshot));
2354
2355   const char* stub_path[] = {
2356     "::(GC roots)",
2357     "::(Strong roots)",
2358     "code_stubs::",
2359     "::(ArraySingleArgumentConstructorStub code)"
2360   };
2361   const v8::HeapGraphNode* node = GetNodeByPath(snapshot,
2362       stub_path, arraysize(stub_path));
2363   CHECK(node);
2364
2365   const char* builtin_path1[] = {"::(GC roots)", "::(Builtins)",
2366                                  "::(KeyedLoadIC_Megamorphic builtin)"};
2367   node = GetNodeByPath(snapshot, builtin_path1, arraysize(builtin_path1));
2368   CHECK(node);
2369
2370   const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)",
2371                                  "::(CompileLazy builtin)"};
2372   node = GetNodeByPath(snapshot, builtin_path2, arraysize(builtin_path2));
2373   CHECK(node);
2374   v8::String::Utf8Value node_name(node->GetName());
2375   CHECK_EQ(0, strcmp("(CompileLazy builtin)", *node_name));
2376 }
2377
2378
2379 static const char* record_trace_tree_source =
2380 "var topFunctions = [];\n"
2381 "var global = this;\n"
2382 "function generateFunctions(width, depth) {\n"
2383 "  var script = [];\n"
2384 "  for (var i = 0; i < width; i++) {\n"
2385 "    for (var j = 0; j < depth; j++) {\n"
2386 "      script.push('function f_' + i + '_' + j + '(x) {\\n');\n"
2387 "      script.push('  try {\\n');\n"
2388 "      if (j < depth-2) {\n"
2389 "        script.push('    return f_' + i + '_' + (j+1) + '(x+1);\\n');\n"
2390 "      } else if (j == depth - 2) {\n"
2391 "        script.push('    return new f_' + i + '_' + (depth - 1) + '();\\n');\n"
2392 "      } else if (j == depth - 1) {\n"
2393 "        script.push('    this.ts = Date.now();\\n');\n"
2394 "      }\n"
2395 "      script.push('  } catch (e) {}\\n');\n"
2396 "      script.push('}\\n');\n"
2397 "      \n"
2398 "    }\n"
2399 "  }\n"
2400 "  var script = script.join('');\n"
2401 "  // throw script;\n"
2402 "  global.eval(script);\n"
2403 "  for (var i = 0; i < width; i++) {\n"
2404 "    topFunctions.push(this['f_' + i + '_0']);\n"
2405 "  }\n"
2406 "}\n"
2407 "\n"
2408 "var width = 3;\n"
2409 "var depth = 3;\n"
2410 "generateFunctions(width, depth);\n"
2411 "var instances = [];\n"
2412 "function start() {\n"
2413 "  for (var i = 0; i < width; i++) {\n"
2414 "    instances.push(topFunctions[i](0));\n"
2415 "  }\n"
2416 "}\n"
2417 "\n"
2418 "for (var i = 0; i < 100; i++) start();\n";
2419
2420
2421 static AllocationTraceNode* FindNode(
2422     AllocationTracker* tracker, const Vector<const char*>& names) {
2423   AllocationTraceNode* node = tracker->trace_tree()->root();
2424   for (int i = 0; node != NULL && i < names.length(); i++) {
2425     const char* name = names[i];
2426     Vector<AllocationTraceNode*> children = node->children();
2427     node = NULL;
2428     for (int j = 0; j < children.length(); j++) {
2429       unsigned index = children[j]->function_info_index();
2430       AllocationTracker::FunctionInfo* info =
2431           tracker->function_info_list()[index];
2432       if (info && strcmp(info->name, name) == 0) {
2433         node = children[j];
2434         break;
2435       }
2436     }
2437   }
2438   return node;
2439 }
2440
2441
2442 TEST(ArrayGrowLeftTrim) {
2443   LocalContext env;
2444   v8::HandleScope scope(env->GetIsolate());
2445   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2446   heap_profiler->StartTrackingHeapObjects(true);
2447
2448   CompileRun(
2449     "var a = [];\n"
2450     "for (var i = 0; i < 5; ++i)\n"
2451     "    a[i] = i;\n"
2452     "for (var i = 0; i < 3; ++i)\n"
2453     "    a.shift();\n");
2454
2455   const char* names[] = {""};
2456   AllocationTracker* tracker =
2457       reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2458   CHECK(tracker);
2459   // Resolve all function locations.
2460   tracker->PrepareForSerialization();
2461   // Print for better diagnostics in case of failure.
2462   tracker->trace_tree()->Print(tracker);
2463
2464   AllocationTraceNode* node =
2465       FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2466   CHECK(node);
2467   CHECK_GE(node->allocation_count(), 2u);
2468   CHECK_GE(node->allocation_size(), 4u * 5u);
2469   heap_profiler->StopTrackingHeapObjects();
2470 }
2471
2472
2473 TEST(TrackHeapAllocations) {
2474   v8::HandleScope scope(v8::Isolate::GetCurrent());
2475   LocalContext env;
2476
2477   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2478   heap_profiler->StartTrackingHeapObjects(true);
2479
2480   CompileRun(record_trace_tree_source);
2481
2482   AllocationTracker* tracker =
2483       reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2484   CHECK(tracker);
2485   // Resolve all function locations.
2486   tracker->PrepareForSerialization();
2487   // Print for better diagnostics in case of failure.
2488   tracker->trace_tree()->Print(tracker);
2489
2490   const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
2491   AllocationTraceNode* node =
2492       FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2493   CHECK(node);
2494   CHECK_GE(node->allocation_count(), 100u);
2495   CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2496   heap_profiler->StopTrackingHeapObjects();
2497 }
2498
2499
2500 static const char* inline_heap_allocation_source =
2501 "function f_0(x) {\n"
2502 "  return f_1(x+1);\n"
2503 "}\n"
2504 "%NeverOptimizeFunction(f_0);\n"
2505 "function f_1(x) {\n"
2506 "  return new f_2(x+1);\n"
2507 "}\n"
2508 "function f_2(x) {\n"
2509 "  this.foo = x;\n"
2510 "}\n"
2511 "var instances = [];\n"
2512 "function start() {\n"
2513 "  instances.push(f_0(0));\n"
2514 "}\n"
2515 "\n"
2516 "for (var i = 0; i < 100; i++) start();\n";
2517
2518
2519 TEST(TrackBumpPointerAllocations) {
2520   i::FLAG_allow_natives_syntax = true;
2521   v8::HandleScope scope(v8::Isolate::GetCurrent());
2522   LocalContext env;
2523
2524   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2525   const char* names[] = {"", "start", "f_0", "f_1"};
2526   // First check that normally all allocations are recorded.
2527   {
2528     heap_profiler->StartTrackingHeapObjects(true);
2529
2530     CompileRun(inline_heap_allocation_source);
2531
2532     AllocationTracker* tracker =
2533         reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2534     CHECK(tracker);
2535     // Resolve all function locations.
2536     tracker->PrepareForSerialization();
2537     // Print for better diagnostics in case of failure.
2538     tracker->trace_tree()->Print(tracker);
2539
2540     AllocationTraceNode* node =
2541         FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2542     CHECK(node);
2543     CHECK_GE(node->allocation_count(), 100u);
2544     CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2545     heap_profiler->StopTrackingHeapObjects();
2546   }
2547
2548   {
2549     heap_profiler->StartTrackingHeapObjects(true);
2550
2551     // Now check that not all allocations are tracked if we manually reenable
2552     // inline allocations.
2553     CHECK(CcTest::heap()->inline_allocation_disabled());
2554     CcTest::heap()->EnableInlineAllocation();
2555
2556     CompileRun(inline_heap_allocation_source);
2557
2558     AllocationTracker* tracker =
2559         reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2560     CHECK(tracker);
2561     // Resolve all function locations.
2562     tracker->PrepareForSerialization();
2563     // Print for better diagnostics in case of failure.
2564     tracker->trace_tree()->Print(tracker);
2565
2566     AllocationTraceNode* node =
2567         FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2568     CHECK(node);
2569     CHECK_LT(node->allocation_count(), 100u);
2570
2571     CcTest::heap()->DisableInlineAllocation();
2572     heap_profiler->StopTrackingHeapObjects();
2573   }
2574 }
2575
2576
2577 TEST(TrackV8ApiAllocation) {
2578   v8::HandleScope scope(v8::Isolate::GetCurrent());
2579   LocalContext env;
2580
2581   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2582   const char* names[] = { "(V8 API)" };
2583   heap_profiler->StartTrackingHeapObjects(true);
2584
2585   v8::Handle<v8::Object> o1 = v8::Object::New(env->GetIsolate());
2586   o1->Clone();
2587
2588   AllocationTracker* tracker =
2589       reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2590   CHECK(tracker);
2591   // Resolve all function locations.
2592   tracker->PrepareForSerialization();
2593   // Print for better diagnostics in case of failure.
2594   tracker->trace_tree()->Print(tracker);
2595
2596   AllocationTraceNode* node =
2597       FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2598   CHECK(node);
2599   CHECK_GE(node->allocation_count(), 2u);
2600   CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2601   heap_profiler->StopTrackingHeapObjects();
2602 }
2603
2604
2605 TEST(ArrayBufferAndArrayBufferView) {
2606   LocalContext env;
2607   v8::HandleScope scope(env->GetIsolate());
2608   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2609   CompileRun("arr1 = new Uint32Array(100);\n");
2610   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2611   CHECK(ValidateSnapshot(snapshot));
2612   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2613   const v8::HeapGraphNode* arr1_obj =
2614       GetProperty(global, v8::HeapGraphEdge::kProperty, "arr1");
2615   CHECK(arr1_obj);
2616   const v8::HeapGraphNode* arr1_buffer =
2617       GetProperty(arr1_obj, v8::HeapGraphEdge::kInternal, "buffer");
2618   CHECK(arr1_buffer);
2619   const v8::HeapGraphNode* backing_store =
2620       GetProperty(arr1_buffer, v8::HeapGraphEdge::kInternal, "backing_store");
2621   CHECK(backing_store);
2622   CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize()));
2623 }
2624
2625
2626 static int GetRetainersCount(const v8::HeapSnapshot* snapshot,
2627                              const v8::HeapGraphNode* node) {
2628   int count = 0;
2629   for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) {
2630     const v8::HeapGraphNode* parent = snapshot->GetNode(i);
2631     for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) {
2632       if (parent->GetChild(j)->GetToNode() == node) {
2633         ++count;
2634       }
2635     }
2636   }
2637   return count;
2638 }
2639
2640
2641 TEST(ArrayBufferSharedBackingStore) {
2642   LocalContext env;
2643   v8::Isolate* isolate = env->GetIsolate();
2644   v8::HandleScope handle_scope(isolate);
2645   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2646
2647   v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
2648   CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
2649   CHECK(!ab->IsExternal());
2650   v8::ArrayBuffer::Contents ab_contents = ab->Externalize();
2651   CHECK(ab->IsExternal());
2652
2653   CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
2654   void* data = ab_contents.Data();
2655   DCHECK(data != NULL);
2656   v8::Local<v8::ArrayBuffer> ab2 =
2657       v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength());
2658   CHECK(ab2->IsExternal());
2659   env->Global()->Set(v8_str("ab1"), ab);
2660   env->Global()->Set(v8_str("ab2"), ab2);
2661
2662   v8::Handle<v8::Value> result = CompileRun("ab2.byteLength");
2663   CHECK_EQ(1024, result->Int32Value());
2664
2665   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2666   CHECK(ValidateSnapshot(snapshot));
2667   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2668   const v8::HeapGraphNode* ab1_node =
2669       GetProperty(global, v8::HeapGraphEdge::kProperty, "ab1");
2670   CHECK(ab1_node);
2671   const v8::HeapGraphNode* ab1_data =
2672       GetProperty(ab1_node, v8::HeapGraphEdge::kInternal, "backing_store");
2673   CHECK(ab1_data);
2674   const v8::HeapGraphNode* ab2_node =
2675       GetProperty(global, v8::HeapGraphEdge::kProperty, "ab2");
2676   CHECK(ab2_node);
2677   const v8::HeapGraphNode* ab2_data =
2678       GetProperty(ab2_node, v8::HeapGraphEdge::kInternal, "backing_store");
2679   CHECK(ab2_data);
2680   CHECK_EQ(ab1_data, ab2_data);
2681   CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data));
2682   free(data);
2683 }
2684
2685
2686 TEST(BoxObject) {
2687   v8::Isolate* isolate = CcTest::isolate();
2688   v8::HandleScope scope(isolate);
2689   LocalContext env;
2690   v8::Handle<v8::Object> global_proxy = env->Global();
2691   v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
2692
2693   i::Factory* factory = CcTest::i_isolate()->factory();
2694   i::Handle<i::String> string = factory->NewStringFromStaticChars("string");
2695   i::Handle<i::Object> box = factory->NewBox(string);
2696   global->Set(0, v8::ToApiHandle<v8::Object>(box));
2697
2698   v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2699   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2700   CHECK(ValidateSnapshot(snapshot));
2701   const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
2702   const v8::HeapGraphNode* box_node =
2703       GetProperty(global_node, v8::HeapGraphEdge::kElement, "0");
2704   CHECK(box_node);
2705   v8::String::Utf8Value box_node_name(box_node->GetName());
2706   CHECK_EQ(0, strcmp("system / Box", *box_node_name));
2707   const v8::HeapGraphNode* box_value =
2708       GetProperty(box_node, v8::HeapGraphEdge::kInternal, "value");
2709   CHECK(box_value);
2710 }
2711
2712
2713 TEST(WeakContainers) {
2714   i::FLAG_allow_natives_syntax = true;
2715   LocalContext env;
2716   v8::HandleScope scope(env->GetIsolate());
2717   if (!CcTest::i_isolate()->use_crankshaft()) return;
2718   v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2719   CompileRun(
2720       "function foo(a) { return a.x; }\n"
2721       "obj = {x : 123};\n"
2722       "foo(obj);\n"
2723       "foo(obj);\n"
2724       "%OptimizeFunctionOnNextCall(foo);\n"
2725       "foo(obj);\n");
2726   const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2727   CHECK(ValidateSnapshot(snapshot));
2728   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2729   const v8::HeapGraphNode* obj =
2730       GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
2731   CHECK(obj);
2732   const v8::HeapGraphNode* map =
2733       GetProperty(obj, v8::HeapGraphEdge::kInternal, "map");
2734   CHECK(map);
2735   const v8::HeapGraphNode* dependent_code =
2736       GetProperty(map, v8::HeapGraphEdge::kInternal, "dependent_code");
2737   if (!dependent_code) return;
2738   int count = dependent_code->GetChildrenCount();
2739   CHECK_NE(0, count);
2740   for (int i = 0; i < count; ++i) {
2741     const v8::HeapGraphEdge* prop = dependent_code->GetChild(i);
2742     CHECK_EQ(v8::HeapGraphEdge::kWeak, prop->GetType());
2743   }
2744 }
2745
2746
2747 static inline i::Address ToAddress(int n) {
2748   return reinterpret_cast<i::Address>(n);
2749 }
2750
2751
2752 TEST(AddressToTraceMap) {
2753   i::AddressToTraceMap map;
2754
2755   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(150)));
2756
2757   // [0x100, 0x200) -> 1
2758   map.AddRange(ToAddress(0x100), 0x100, 1U);
2759   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x50)));
2760   CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x100)));
2761   CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x150)));
2762   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x100 + 0x100)));
2763   CHECK_EQ(1u, map.size());
2764
2765   // [0x100, 0x200) -> 1, [0x200, 0x300) -> 2
2766   map.AddRange(ToAddress(0x200), 0x100, 2U);
2767   CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x2a0)));
2768   CHECK_EQ(2u, map.size());
2769
2770   // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2
2771   map.AddRange(ToAddress(0x180), 0x100, 3U);
2772   CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
2773   CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
2774   CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
2775   CHECK_EQ(3u, map.size());
2776
2777   // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2,
2778   // [0x400, 0x500) -> 4
2779   map.AddRange(ToAddress(0x400), 0x100, 4U);
2780   CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
2781   CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
2782   CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
2783   CHECK_EQ(4u, map.GetTraceNodeId(ToAddress(0x450)));
2784   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x500)));
2785   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x350)));
2786   CHECK_EQ(4u, map.size());
2787
2788   // [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5
2789   map.AddRange(ToAddress(0x200), 0x400, 5U);
2790   CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
2791   CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x400)));
2792   CHECK_EQ(3u, map.size());
2793
2794   // [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5
2795   map.AddRange(ToAddress(0x180), 0x80, 6U);
2796   map.AddRange(ToAddress(0x180), 0x80, 7U);
2797   CHECK_EQ(7u, map.GetTraceNodeId(ToAddress(0x180)));
2798   CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
2799   CHECK_EQ(3u, map.size());
2800
2801   map.Clear();
2802   CHECK_EQ(0u, map.size());
2803   CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x400)));
2804 }