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