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