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