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