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