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