• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 //
3 // Tests for heap profiler
4 
5 #include "v8.h"
6 
7 #include "cctest.h"
8 #include "heap-profiler.h"
9 #include "snapshot.h"
10 #include "utils-inl.h"
11 #include "../include/v8-profiler.h"
12 
13 namespace {
14 
15 class NamedEntriesDetector {
16  public:
NamedEntriesDetector()17   NamedEntriesDetector()
18       : has_A2(false), has_B2(false), has_C2(false) {
19   }
20 
CheckEntry(i::HeapEntry * entry)21   void CheckEntry(i::HeapEntry* entry) {
22     if (strcmp(entry->name(), "A2") == 0) has_A2 = true;
23     if (strcmp(entry->name(), "B2") == 0) has_B2 = true;
24     if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
25   }
26 
CheckAllReachables(i::HeapEntry * root)27   void CheckAllReachables(i::HeapEntry* root) {
28     i::List<i::HeapEntry*> list(10);
29     list.Add(root);
30     root->paint();
31     CheckEntry(root);
32     while (!list.is_empty()) {
33       i::HeapEntry* entry = list.RemoveLast();
34       i::Vector<i::HeapGraphEdge> children = entry->children();
35       for (int i = 0; i < children.length(); ++i) {
36         if (children[i].type() == i::HeapGraphEdge::kShortcut) continue;
37         i::HeapEntry* child = children[i].to();
38         if (!child->painted()) {
39           list.Add(child);
40           child->paint();
41           CheckEntry(child);
42         }
43       }
44     }
45   }
46 
47   bool has_A2;
48   bool has_B2;
49   bool has_C2;
50 };
51 
52 }  // namespace
53 
54 
GetGlobalObject(const v8::HeapSnapshot * snapshot)55 static const v8::HeapGraphNode* GetGlobalObject(
56     const v8::HeapSnapshot* snapshot) {
57   CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount());
58   const v8::HeapGraphNode* global_obj =
59       snapshot->GetRoot()->GetChild(0)->GetToNode();
60   CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
61       reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
62   return global_obj;
63 }
64 
65 
GetProperty(const v8::HeapGraphNode * node,v8::HeapGraphEdge::Type type,const char * name)66 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
67                                             v8::HeapGraphEdge::Type type,
68                                             const char* name) {
69   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
70     const v8::HeapGraphEdge* prop = node->GetChild(i);
71     v8::String::AsciiValue prop_name(prop->GetName());
72     if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
73       return prop->GetToNode();
74   }
75   return NULL;
76 }
77 
78 
HasString(const v8::HeapGraphNode * node,const char * contents)79 static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
80   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
81     const v8::HeapGraphEdge* prop = node->GetChild(i);
82     const v8::HeapGraphNode* node = prop->GetToNode();
83     if (node->GetType() == v8::HeapGraphNode::kString) {
84       v8::String::AsciiValue node_name(node->GetName());
85       if (strcmp(contents, *node_name) == 0) return true;
86     }
87   }
88   return false;
89 }
90 
91 
TEST(HeapSnapshot)92 TEST(HeapSnapshot) {
93   v8::HandleScope scope;
94   LocalContext env2;
95 
96   CompileRun(
97       "function A2() {}\n"
98       "function B2(x) { return function() { return typeof x; }; }\n"
99       "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
100       "var a2 = new A2();\n"
101       "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
102       "var c2 = new C2(a2);");
103   const v8::HeapSnapshot* snapshot_env2 =
104       v8::HeapProfiler::TakeSnapshot(v8_str("env2"));
105   i::HeapSnapshot* i_snapshot_env2 =
106       const_cast<i::HeapSnapshot*>(
107           reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
108   const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
109 
110   // Verify, that JS global object of env2 has '..2' properties.
111   const v8::HeapGraphNode* a2_node =
112       GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "a2");
113   CHECK_NE(NULL, a2_node);
114   CHECK_NE(
115       NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1"));
116   CHECK_NE(
117       NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2"));
118   CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2"));
119 
120   // Paint all nodes reachable from global object.
121   NamedEntriesDetector det;
122   i_snapshot_env2->ClearPaint();
123   det.CheckAllReachables(const_cast<i::HeapEntry*>(
124       reinterpret_cast<const i::HeapEntry*>(global_env2)));
125   CHECK(det.has_A2);
126   CHECK(det.has_B2);
127   CHECK(det.has_C2);
128 }
129 
130 
TEST(HeapSnapshotObjectSizes)131 TEST(HeapSnapshotObjectSizes) {
132   v8::HandleScope scope;
133   LocalContext env;
134 
135   //   -a-> X1 --a
136   // x -b-> X2 <-|
137   CompileRun(
138       "function X(a, b) { this.a = a; this.b = b; }\n"
139       "x = new X(new X(), new X());\n"
140       "(function() { x.a.a = x.b; })();");
141   const v8::HeapSnapshot* snapshot =
142       v8::HeapProfiler::TakeSnapshot(v8_str("sizes"));
143   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
144   const v8::HeapGraphNode* x =
145       GetProperty(global, v8::HeapGraphEdge::kShortcut, "x");
146   CHECK_NE(NULL, x);
147   const v8::HeapGraphNode* x1 =
148       GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
149   CHECK_NE(NULL, x1);
150   const v8::HeapGraphNode* x2 =
151       GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
152   CHECK_NE(NULL, x2);
153 
154   // Test sizes.
155   CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize());
156   CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize());
157   CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize());
158 }
159 
160 
TEST(BoundFunctionInSnapshot)161 TEST(BoundFunctionInSnapshot) {
162   v8::HandleScope scope;
163   LocalContext env;
164   CompileRun(
165       "function myFunction(a, b) { this.a = a; this.b = b; }\n"
166       "function AAAAA() {}\n"
167       "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
168   const v8::HeapSnapshot* snapshot =
169       v8::HeapProfiler::TakeSnapshot(v8_str("sizes"));
170   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
171   const v8::HeapGraphNode* f =
172       GetProperty(global, v8::HeapGraphEdge::kShortcut, "boundFunction");
173   CHECK(f);
174   CHECK_EQ(v8::String::New("native_bind"), f->GetName());
175   const v8::HeapGraphNode* bindings =
176       GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings");
177   CHECK_NE(NULL, bindings);
178   CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
179   CHECK_EQ(4, bindings->GetChildrenCount());
180 
181   const v8::HeapGraphNode* bound_this = GetProperty(
182       f, v8::HeapGraphEdge::kShortcut, "bound_this");
183   CHECK(bound_this);
184   CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());
185 
186   const v8::HeapGraphNode* bound_function = GetProperty(
187       f, v8::HeapGraphEdge::kShortcut, "bound_function");
188   CHECK(bound_function);
189   CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());
190 
191   const v8::HeapGraphNode* bound_argument = GetProperty(
192       f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
193   CHECK(bound_argument);
194   CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
195 }
196 
197 
TEST(HeapSnapshotEntryChildren)198 TEST(HeapSnapshotEntryChildren) {
199   v8::HandleScope scope;
200   LocalContext env;
201 
202   CompileRun(
203       "function A() { }\n"
204       "a = new A;");
205   const v8::HeapSnapshot* snapshot =
206       v8::HeapProfiler::TakeSnapshot(v8_str("children"));
207   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
208   for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
209     const v8::HeapGraphEdge* prop = global->GetChild(i);
210     CHECK_EQ(global, prop->GetFromNode());
211   }
212   const v8::HeapGraphNode* a =
213       GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
214   CHECK_NE(NULL, a);
215   for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
216     const v8::HeapGraphEdge* prop = a->GetChild(i);
217     CHECK_EQ(a, prop->GetFromNode());
218   }
219 }
220 
221 
TEST(HeapSnapshotCodeObjects)222 TEST(HeapSnapshotCodeObjects) {
223   v8::HandleScope scope;
224   LocalContext env;
225 
226   CompileRun(
227       "function lazy(x) { return x - 1; }\n"
228       "function compiled(x) { return x + 1; }\n"
229       "var anonymous = (function() { return function() { return 0; } })();\n"
230       "compiled(1)");
231   const v8::HeapSnapshot* snapshot =
232       v8::HeapProfiler::TakeSnapshot(v8_str("code"));
233 
234   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
235   const v8::HeapGraphNode* compiled =
236       GetProperty(global, v8::HeapGraphEdge::kShortcut, "compiled");
237   CHECK_NE(NULL, compiled);
238   CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
239   const v8::HeapGraphNode* lazy =
240       GetProperty(global, v8::HeapGraphEdge::kShortcut, "lazy");
241   CHECK_NE(NULL, lazy);
242   CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
243   const v8::HeapGraphNode* anonymous =
244       GetProperty(global, v8::HeapGraphEdge::kShortcut, "anonymous");
245   CHECK_NE(NULL, anonymous);
246   CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
247   v8::String::AsciiValue anonymous_name(anonymous->GetName());
248   CHECK_EQ("", *anonymous_name);
249 
250   // Find references to code.
251   const v8::HeapGraphNode* compiled_code =
252       GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared");
253   CHECK_NE(NULL, compiled_code);
254   const v8::HeapGraphNode* lazy_code =
255       GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
256   CHECK_NE(NULL, lazy_code);
257 
258   // Verify that non-compiled code doesn't contain references to "x"
259   // literal, while compiled code does. The scope info is stored in FixedArray
260   // objects attached to the SharedFunctionInfo.
261   bool compiled_references_x = false, lazy_references_x = false;
262   for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
263     const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
264     const v8::HeapGraphNode* node = prop->GetToNode();
265     if (node->GetType() == v8::HeapGraphNode::kArray) {
266       if (HasString(node, "x")) {
267         compiled_references_x = true;
268         break;
269       }
270     }
271   }
272   for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
273     const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
274     const v8::HeapGraphNode* node = prop->GetToNode();
275     if (node->GetType() == v8::HeapGraphNode::kArray) {
276       if (HasString(node, "x")) {
277         lazy_references_x = true;
278         break;
279       }
280     }
281   }
282   CHECK(compiled_references_x);
283   CHECK(!lazy_references_x);
284 }
285 
286 
TEST(HeapSnapshotHeapNumbers)287 TEST(HeapSnapshotHeapNumbers) {
288   v8::HandleScope scope;
289   LocalContext env;
290   CompileRun(
291       "a = 1;    // a is Smi\n"
292       "b = 2.5;  // b is HeapNumber");
293   const v8::HeapSnapshot* snapshot =
294       v8::HeapProfiler::TakeSnapshot(v8_str("numbers"));
295   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
296   CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kShortcut, "a"));
297   const v8::HeapGraphNode* b =
298       GetProperty(global, v8::HeapGraphEdge::kShortcut, "b");
299   CHECK_NE(NULL, b);
300   CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
301 }
302 
TEST(HeapSnapshotSlicedString)303 TEST(HeapSnapshotSlicedString) {
304   v8::HandleScope scope;
305   LocalContext env;
306   CompileRun(
307       "parent_string = \"123456789.123456789.123456789.123456789.123456789."
308       "123456789.123456789.123456789.123456789.123456789."
309       "123456789.123456789.123456789.123456789.123456789."
310       "123456789.123456789.123456789.123456789.123456789.\";"
311       "child_string = parent_string.slice(100);");
312   const v8::HeapSnapshot* snapshot =
313       v8::HeapProfiler::TakeSnapshot(v8_str("strings"));
314   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
315   const v8::HeapGraphNode* parent_string =
316       GetProperty(global, v8::HeapGraphEdge::kShortcut, "parent_string");
317   CHECK_NE(NULL, parent_string);
318   const v8::HeapGraphNode* child_string =
319       GetProperty(global, v8::HeapGraphEdge::kShortcut, "child_string");
320   CHECK_NE(NULL, child_string);
321   const v8::HeapGraphNode* parent =
322       GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent");
323   CHECK_EQ(parent_string, parent);
324 }
325 
TEST(HeapSnapshotInternalReferences)326 TEST(HeapSnapshotInternalReferences) {
327   v8::HandleScope scope;
328   v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
329   global_template->SetInternalFieldCount(2);
330   LocalContext env(NULL, global_template);
331   v8::Handle<v8::Object> global_proxy = env->Global();
332   v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
333   CHECK_EQ(2, global->InternalFieldCount());
334   v8::Local<v8::Object> obj = v8::Object::New();
335   global->SetInternalField(0, v8_num(17));
336   global->SetInternalField(1, obj);
337   const v8::HeapSnapshot* snapshot =
338       v8::HeapProfiler::TakeSnapshot(v8_str("internals"));
339   const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
340   // The first reference will not present, because it's a Smi.
341   CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
342   // The second reference is to an object.
343   CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
344 }
345 
346 
347 // Trying to introduce a check helper for uint64_t causes many
348 // overloading ambiguities, so it seems easier just to cast
349 // them to a signed type.
350 #define CHECK_EQ_UINT64_T(a, b) \
351   CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b))
352 #define CHECK_NE_UINT64_T(a, b) \
353   CHECK((a) != (b))  // NOLINT
354 
TEST(HeapEntryIdsAndArrayShift)355 TEST(HeapEntryIdsAndArrayShift) {
356   v8::HandleScope scope;
357   LocalContext env;
358 
359   CompileRun(
360       "function AnObject() {\n"
361       "    this.first = 'first';\n"
362       "    this.second = 'second';\n"
363       "}\n"
364       "var a = new Array();\n"
365       "for (var i = 0; i < 10; ++i)\n"
366       "  a.push(new AnObject());\n");
367   const v8::HeapSnapshot* snapshot1 =
368       v8::HeapProfiler::TakeSnapshot(v8_str("s1"));
369 
370   CompileRun(
371       "for (var i = 0; i < 1; ++i)\n"
372       "  a.shift();\n");
373 
374   HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
375 
376   const v8::HeapSnapshot* snapshot2 =
377       v8::HeapProfiler::TakeSnapshot(v8_str("s2"));
378 
379   const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
380   const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
381   CHECK_NE_UINT64_T(0, global1->GetId());
382   CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
383 
384   const v8::HeapGraphNode* a1 =
385       GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
386   CHECK_NE(NULL, a1);
387   const v8::HeapGraphNode* e1 =
388       GetProperty(a1, v8::HeapGraphEdge::kHidden, "1");
389   CHECK_NE(NULL, e1);
390   const v8::HeapGraphNode* k1 =
391       GetProperty(e1, v8::HeapGraphEdge::kInternal, "elements");
392   CHECK_NE(NULL, k1);
393   const v8::HeapGraphNode* a2 =
394       GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
395   CHECK_NE(NULL, a2);
396   const v8::HeapGraphNode* e2 =
397       GetProperty(a2, v8::HeapGraphEdge::kHidden, "1");
398   CHECK_NE(NULL, e2);
399   const v8::HeapGraphNode* k2 =
400       GetProperty(e2, v8::HeapGraphEdge::kInternal, "elements");
401   CHECK_NE(NULL, k2);
402 
403   CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
404   CHECK_EQ_UINT64_T(e1->GetId(), e2->GetId());
405   CHECK_EQ_UINT64_T(k1->GetId(), k2->GetId());
406 }
407 
TEST(HeapEntryIdsAndGC)408 TEST(HeapEntryIdsAndGC) {
409   v8::HandleScope scope;
410   LocalContext env;
411 
412   CompileRun(
413       "function A() {}\n"
414       "function B(x) { this.x = x; }\n"
415       "var a = new A();\n"
416       "var b = new B(a);");
417   const v8::HeapSnapshot* snapshot1 =
418       v8::HeapProfiler::TakeSnapshot(v8_str("s1"));
419 
420   HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
421 
422   const v8::HeapSnapshot* snapshot2 =
423       v8::HeapProfiler::TakeSnapshot(v8_str("s2"));
424 
425   const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
426   const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
427   CHECK_NE_UINT64_T(0, global1->GetId());
428   CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
429   const v8::HeapGraphNode* A1 =
430       GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
431   CHECK_NE(NULL, A1);
432   const v8::HeapGraphNode* A2 =
433       GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
434   CHECK_NE(NULL, A2);
435   CHECK_NE_UINT64_T(0, A1->GetId());
436   CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId());
437   const v8::HeapGraphNode* B1 =
438       GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
439   CHECK_NE(NULL, B1);
440   const v8::HeapGraphNode* B2 =
441       GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
442   CHECK_NE(NULL, B2);
443   CHECK_NE_UINT64_T(0, B1->GetId());
444   CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId());
445   const v8::HeapGraphNode* a1 =
446       GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
447   CHECK_NE(NULL, a1);
448   const v8::HeapGraphNode* a2 =
449       GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
450   CHECK_NE(NULL, a2);
451   CHECK_NE_UINT64_T(0, a1->GetId());
452   CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
453   const v8::HeapGraphNode* b1 =
454       GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
455   CHECK_NE(NULL, b1);
456   const v8::HeapGraphNode* b2 =
457       GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
458   CHECK_NE(NULL, b2);
459   CHECK_NE_UINT64_T(0, b1->GetId());
460   CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId());
461 }
462 
463 
TEST(HeapSnapshotRootPreservedAfterSorting)464 TEST(HeapSnapshotRootPreservedAfterSorting) {
465   v8::HandleScope scope;
466   LocalContext env;
467   const v8::HeapSnapshot* snapshot =
468       v8::HeapProfiler::TakeSnapshot(v8_str("s"));
469   const v8::HeapGraphNode* root1 = snapshot->GetRoot();
470   const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
471       snapshot))->GetSortedEntriesList();
472   const v8::HeapGraphNode* root2 = snapshot->GetRoot();
473   CHECK_EQ(root1, root2);
474 }
475 
476 
TEST(HeapEntryDominator)477 TEST(HeapEntryDominator) {
478   // The graph looks like this:
479   //
480   //                   -> node1
481   //                  a    |^
482   //          -> node5     ba
483   //         a             v|
484   //   node6           -> node2
485   //         b        a    |^
486   //          -> node4     ba
487   //                  b    v|
488   //                   -> node3
489   //
490   // The dominator for all nodes is node6.
491 
492   v8::HandleScope scope;
493   LocalContext env;
494 
495   CompileRun(
496       "function X(a, b) { this.a = a; this.b = b; }\n"
497       "node6 = new X(new X(new X()), new X(new X(),new X()));\n"
498       "(function(){\n"
499       "node6.a.a.b = node6.b.a;  // node1 -> node2\n"
500       "node6.b.a.a = node6.a.a;  // node2 -> node1\n"
501       "node6.b.a.b = node6.b.b;  // node2 -> node3\n"
502       "node6.b.b.a = node6.b.a;  // node3 -> node2\n"
503       "})();");
504 
505   const v8::HeapSnapshot* snapshot =
506       v8::HeapProfiler::TakeSnapshot(v8_str("dominators"));
507 
508   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
509   CHECK_NE(NULL, global);
510   const v8::HeapGraphNode* node6 =
511       GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6");
512   CHECK_NE(NULL, node6);
513   const v8::HeapGraphNode* node5 =
514       GetProperty(node6, v8::HeapGraphEdge::kProperty, "a");
515   CHECK_NE(NULL, node5);
516   const v8::HeapGraphNode* node4 =
517       GetProperty(node6, v8::HeapGraphEdge::kProperty, "b");
518   CHECK_NE(NULL, node4);
519   const v8::HeapGraphNode* node3 =
520       GetProperty(node4, v8::HeapGraphEdge::kProperty, "b");
521   CHECK_NE(NULL, node3);
522   const v8::HeapGraphNode* node2 =
523       GetProperty(node4, v8::HeapGraphEdge::kProperty, "a");
524   CHECK_NE(NULL, node2);
525   const v8::HeapGraphNode* node1 =
526       GetProperty(node5, v8::HeapGraphEdge::kProperty, "a");
527   CHECK_NE(NULL, node1);
528 
529   CHECK_EQ(node6, node1->GetDominatorNode());
530   CHECK_EQ(node6, node2->GetDominatorNode());
531   CHECK_EQ(node6, node3->GetDominatorNode());
532   CHECK_EQ(node6, node4->GetDominatorNode());
533   CHECK_EQ(node6, node5->GetDominatorNode());
534 }
535 
536 
537 namespace {
538 
539 class TestJSONStream : public v8::OutputStream {
540  public:
TestJSONStream()541   TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
TestJSONStream(int abort_countdown)542   explicit TestJSONStream(int abort_countdown)
543       : eos_signaled_(0), abort_countdown_(abort_countdown) {}
~TestJSONStream()544   virtual ~TestJSONStream() {}
EndOfStream()545   virtual void EndOfStream() { ++eos_signaled_; }
WriteAsciiChunk(char * buffer,int chars_written)546   virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
547     if (abort_countdown_ > 0) --abort_countdown_;
548     if (abort_countdown_ == 0) return kAbort;
549     CHECK_GT(chars_written, 0);
550     i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
551     memcpy(chunk.start(), buffer, chars_written);
552     return kContinue;
553   }
WriteTo(i::Vector<char> dest)554   void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
eos_signaled()555   int eos_signaled() { return eos_signaled_; }
size()556   int size() { return buffer_.size(); }
557  private:
558   i::Collector<char> buffer_;
559   int eos_signaled_;
560   int abort_countdown_;
561 };
562 
563 class AsciiResource: public v8::String::ExternalAsciiStringResource {
564  public:
AsciiResource(i::Vector<char> string)565   explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
566     length_ = string.length();
567   }
data() const568   virtual const char* data() const { return data_; }
length() const569   virtual size_t length() const { return length_; }
570  private:
571   const char* data_;
572   size_t length_;
573 };
574 
575 }  // namespace
576 
TEST(HeapSnapshotJSONSerialization)577 TEST(HeapSnapshotJSONSerialization) {
578   v8::HandleScope scope;
579   LocalContext env;
580 
581 #define STRING_LITERAL_FOR_TEST \
582   "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
583   CompileRun(
584       "function A(s) { this.s = s; }\n"
585       "function B(x) { this.x = x; }\n"
586       "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
587       "var b = new B(a);");
588   const v8::HeapSnapshot* snapshot =
589       v8::HeapProfiler::TakeSnapshot(v8_str("json"));
590   TestJSONStream stream;
591   snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
592   CHECK_GT(stream.size(), 0);
593   CHECK_EQ(1, stream.eos_signaled());
594   i::ScopedVector<char> json(stream.size());
595   stream.WriteTo(json);
596 
597   // Verify that snapshot string is valid JSON.
598   AsciiResource json_res(json);
599   v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
600   env->Global()->Set(v8_str("json_snapshot"), json_string);
601   v8::Local<v8::Value> snapshot_parse_result = CompileRun(
602       "var parsed = JSON.parse(json_snapshot); true;");
603   CHECK(!snapshot_parse_result.IsEmpty());
604 
605   // Verify that snapshot object has required fields.
606   v8::Local<v8::Object> parsed_snapshot =
607       env->Global()->Get(v8_str("parsed"))->ToObject();
608   CHECK(parsed_snapshot->Has(v8_str("snapshot")));
609   CHECK(parsed_snapshot->Has(v8_str("nodes")));
610   CHECK(parsed_snapshot->Has(v8_str("strings")));
611 
612   // Get node and edge "member" offsets.
613   v8::Local<v8::Value> meta_analysis_result = CompileRun(
614       "var parsed_meta = parsed.nodes[0];\n"
615       "var children_count_offset ="
616       "    parsed_meta.fields.indexOf('children_count');\n"
617       "var children_offset ="
618       "    parsed_meta.fields.indexOf('children');\n"
619       "var children_meta ="
620       "    parsed_meta.types[children_offset];\n"
621       "var child_fields_count = children_meta.fields.length;\n"
622       "var child_type_offset ="
623       "    children_meta.fields.indexOf('type');\n"
624       "var child_name_offset ="
625       "    children_meta.fields.indexOf('name_or_index');\n"
626       "var child_to_node_offset ="
627       "    children_meta.fields.indexOf('to_node');\n"
628       "var property_type ="
629       "    children_meta.types[child_type_offset].indexOf('property');\n"
630       "var shortcut_type ="
631       "    children_meta.types[child_type_offset].indexOf('shortcut');");
632   CHECK(!meta_analysis_result.IsEmpty());
633 
634   // A helper function for processing encoded nodes.
635   CompileRun(
636       "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
637       "  var nodes = parsed.nodes;\n"
638       "  var strings = parsed.strings;\n"
639       "  for (var i = 0,\n"
640       "      count = nodes[pos + children_count_offset] * child_fields_count;\n"
641       "      i < count; i += child_fields_count) {\n"
642       "    var child_pos = pos + children_offset + i;\n"
643       "    if (nodes[child_pos + child_type_offset] === prop_type\n"
644       "       && strings[nodes[child_pos + child_name_offset]] === prop_name)\n"
645       "        return nodes[child_pos + child_to_node_offset];\n"
646       "  }\n"
647       "  return null;\n"
648       "}\n");
649   // Get the string index using the path: <root> -> <global>.b.x.s
650   v8::Local<v8::Value> string_obj_pos_val = CompileRun(
651       "GetChildPosByProperty(\n"
652       "  GetChildPosByProperty(\n"
653       "    GetChildPosByProperty("
654       "      parsed.nodes[1 + children_offset + child_to_node_offset],"
655       "      \"b\",shortcut_type),\n"
656       "    \"x\", property_type),"
657       "  \"s\", property_type)");
658   CHECK(!string_obj_pos_val.IsEmpty());
659   int string_obj_pos =
660       static_cast<int>(string_obj_pos_val->ToNumber()->Value());
661   v8::Local<v8::Object> nodes_array =
662       parsed_snapshot->Get(v8_str("nodes"))->ToObject();
663   int string_index = static_cast<int>(
664       nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
665   CHECK_GT(string_index, 0);
666   v8::Local<v8::Object> strings_array =
667       parsed_snapshot->Get(v8_str("strings"))->ToObject();
668   v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
669   v8::Local<v8::String> ref_string =
670       CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
671 #undef STRING_LITERAL_FOR_TEST
672   CHECK_EQ(*v8::String::Utf8Value(ref_string),
673            *v8::String::Utf8Value(string));
674 }
675 
676 
TEST(HeapSnapshotJSONSerializationAborting)677 TEST(HeapSnapshotJSONSerializationAborting) {
678   v8::HandleScope scope;
679   LocalContext env;
680   const v8::HeapSnapshot* snapshot =
681       v8::HeapProfiler::TakeSnapshot(v8_str("abort"));
682   TestJSONStream stream(5);
683   snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
684   CHECK_GT(stream.size(), 0);
685   CHECK_EQ(0, stream.eos_signaled());
686 }
687 
688 
CheckChildrenIds(const v8::HeapSnapshot * snapshot,const v8::HeapGraphNode * node,int level,int max_level)689 static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
690                              const v8::HeapGraphNode* node,
691                              int level, int max_level) {
692   if (level > max_level) return;
693   CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
694   for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
695     const v8::HeapGraphEdge* prop = node->GetChild(i);
696     const v8::HeapGraphNode* child =
697         snapshot->GetNodeById(prop->GetToNode()->GetId());
698     CHECK_EQ_UINT64_T(prop->GetToNode()->GetId(), child->GetId());
699     CHECK_EQ(prop->GetToNode(), child);
700     CheckChildrenIds(snapshot, child, level + 1, max_level);
701   }
702 }
703 
704 
TEST(HeapSnapshotGetNodeById)705 TEST(HeapSnapshotGetNodeById) {
706   v8::HandleScope scope;
707   LocalContext env;
708 
709   const v8::HeapSnapshot* snapshot =
710       v8::HeapProfiler::TakeSnapshot(v8_str("id"));
711   const v8::HeapGraphNode* root = snapshot->GetRoot();
712   CheckChildrenIds(snapshot, root, 0, 3);
713   // Check a big id, which should not exist yet.
714   CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL));
715 }
716 
717 
718 namespace {
719 
720 class TestActivityControl : public v8::ActivityControl {
721  public:
TestActivityControl(int abort_count)722   explicit TestActivityControl(int abort_count)
723       : done_(0), total_(0), abort_count_(abort_count) {}
ReportProgressValue(int done,int total)724   ControlOption ReportProgressValue(int done, int total) {
725     done_ = done;
726     total_ = total;
727     return --abort_count_ != 0 ? kContinue : kAbort;
728   }
done()729   int done() { return done_; }
total()730   int total() { return total_; }
731 
732  private:
733   int done_;
734   int total_;
735   int abort_count_;
736 };
737 }
738 
TEST(TakeHeapSnapshotAborting)739 TEST(TakeHeapSnapshotAborting) {
740   v8::HandleScope scope;
741   LocalContext env;
742 
743   const int snapshots_count = v8::HeapProfiler::GetSnapshotsCount();
744   TestActivityControl aborting_control(1);
745   const v8::HeapSnapshot* no_snapshot =
746       v8::HeapProfiler::TakeSnapshot(v8_str("abort"),
747                                      v8::HeapSnapshot::kFull,
748                                      &aborting_control);
749   CHECK_EQ(NULL, no_snapshot);
750   CHECK_EQ(snapshots_count, v8::HeapProfiler::GetSnapshotsCount());
751   CHECK_GT(aborting_control.total(), aborting_control.done());
752 
753   TestActivityControl control(-1);  // Don't abort.
754   const v8::HeapSnapshot* snapshot =
755       v8::HeapProfiler::TakeSnapshot(v8_str("full"),
756                                      v8::HeapSnapshot::kFull,
757                                      &control);
758   CHECK_NE(NULL, snapshot);
759   CHECK_EQ(snapshots_count + 1, v8::HeapProfiler::GetSnapshotsCount());
760   CHECK_EQ(control.total(), control.done());
761   CHECK_GT(control.total(), 0);
762 }
763 
764 
765 namespace {
766 
767 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
768  public:
TestRetainedObjectInfo(int hash,const char * group_label,const char * label,intptr_t element_count=-1,intptr_t size=-1)769   TestRetainedObjectInfo(int hash,
770                          const char* group_label,
771                          const char* label,
772                          intptr_t element_count = -1,
773                          intptr_t size = -1)
774       : disposed_(false),
775         hash_(hash),
776         group_label_(group_label),
777         label_(label),
778         element_count_(element_count),
779         size_(size) {
780     instances.Add(this);
781   }
~TestRetainedObjectInfo()782   virtual ~TestRetainedObjectInfo() {}
Dispose()783   virtual void Dispose() {
784     CHECK(!disposed_);
785     disposed_ = true;
786   }
IsEquivalent(RetainedObjectInfo * other)787   virtual bool IsEquivalent(RetainedObjectInfo* other) {
788     return GetHash() == other->GetHash();
789   }
GetHash()790   virtual intptr_t GetHash() { return hash_; }
GetGroupLabel()791   virtual const char* GetGroupLabel() { return group_label_; }
GetLabel()792   virtual const char* GetLabel() { return label_; }
GetElementCount()793   virtual intptr_t GetElementCount() { return element_count_; }
GetSizeInBytes()794   virtual intptr_t GetSizeInBytes() { return size_; }
disposed()795   bool disposed() { return disposed_; }
796 
WrapperInfoCallback(uint16_t class_id,v8::Handle<v8::Value> wrapper)797   static v8::RetainedObjectInfo* WrapperInfoCallback(
798       uint16_t class_id, v8::Handle<v8::Value> wrapper) {
799     if (class_id == 1) {
800       if (wrapper->IsString()) {
801         v8::String::AsciiValue ascii(wrapper);
802         if (strcmp(*ascii, "AAA") == 0)
803           return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
804         else if (strcmp(*ascii, "BBB") == 0)
805           return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
806       }
807     } else if (class_id == 2) {
808       if (wrapper->IsString()) {
809         v8::String::AsciiValue ascii(wrapper);
810         if (strcmp(*ascii, "CCC") == 0)
811           return new TestRetainedObjectInfo(2, "ccc-group", "ccc");
812       }
813     }
814     CHECK(false);
815     return NULL;
816   }
817 
818   static i::List<TestRetainedObjectInfo*> instances;
819 
820  private:
821   bool disposed_;
822   int category_;
823   int hash_;
824   const char* group_label_;
825   const char* label_;
826   intptr_t element_count_;
827   intptr_t size_;
828 };
829 
830 
831 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
832 }
833 
834 
GetNode(const v8::HeapGraphNode * parent,v8::HeapGraphNode::Type type,const char * name)835 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
836                                         v8::HeapGraphNode::Type type,
837                                         const char* name) {
838   for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
839     const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
840     if (node->GetType() == type && strcmp(name,
841                const_cast<i::HeapEntry*>(
842                    reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
843       return node;
844     }
845   }
846   return NULL;
847 }
848 
849 
TEST(HeapSnapshotRetainedObjectInfo)850 TEST(HeapSnapshotRetainedObjectInfo) {
851   v8::HandleScope scope;
852   LocalContext env;
853 
854   v8::HeapProfiler::DefineWrapperClass(
855       1, TestRetainedObjectInfo::WrapperInfoCallback);
856   v8::HeapProfiler::DefineWrapperClass(
857       2, TestRetainedObjectInfo::WrapperInfoCallback);
858   v8::Persistent<v8::String> p_AAA =
859       v8::Persistent<v8::String>::New(v8_str("AAA"));
860   p_AAA.SetWrapperClassId(1);
861   v8::Persistent<v8::String> p_BBB =
862       v8::Persistent<v8::String>::New(v8_str("BBB"));
863   p_BBB.SetWrapperClassId(1);
864   v8::Persistent<v8::String> p_CCC =
865       v8::Persistent<v8::String>::New(v8_str("CCC"));
866   p_CCC.SetWrapperClassId(2);
867   CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
868   const v8::HeapSnapshot* snapshot =
869       v8::HeapProfiler::TakeSnapshot(v8_str("retained"));
870 
871   CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
872   for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
873     CHECK(TestRetainedObjectInfo::instances[i]->disposed());
874     delete TestRetainedObjectInfo::instances[i];
875   }
876 
877   const v8::HeapGraphNode* native_group_aaa = GetNode(
878       snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
879   CHECK_NE(NULL, native_group_aaa);
880   CHECK_EQ(1, native_group_aaa->GetChildrenCount());
881   const v8::HeapGraphNode* aaa = GetNode(
882       native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries");
883   CHECK_NE(NULL, aaa);
884   CHECK_EQ(2, aaa->GetChildrenCount());
885 
886   const v8::HeapGraphNode* native_group_ccc = GetNode(
887       snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
888   const v8::HeapGraphNode* ccc = GetNode(
889       native_group_ccc, v8::HeapGraphNode::kNative, "ccc");
890   CHECK_NE(NULL, ccc);
891 
892   const v8::HeapGraphNode* n_AAA = GetNode(
893       aaa, v8::HeapGraphNode::kString, "AAA");
894   CHECK_NE(NULL, n_AAA);
895   const v8::HeapGraphNode* n_BBB = GetNode(
896       aaa, v8::HeapGraphNode::kString, "BBB");
897   CHECK_NE(NULL, n_BBB);
898   CHECK_EQ(1, ccc->GetChildrenCount());
899   const v8::HeapGraphNode* n_CCC = GetNode(
900       ccc, v8::HeapGraphNode::kString, "CCC");
901   CHECK_NE(NULL, n_CCC);
902 
903   CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
904   CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
905   CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
906 }
907 
908 
909 class GraphWithImplicitRefs {
910  public:
911   static const int kObjectsCount = 4;
GraphWithImplicitRefs(LocalContext * env)912   explicit GraphWithImplicitRefs(LocalContext* env) {
913     CHECK_EQ(NULL, instance_);
914     instance_ = this;
915     for (int i = 0; i < kObjectsCount; i++) {
916       objects_[i] = v8::Persistent<v8::Object>::New(v8::Object::New());
917     }
918     (*env)->Global()->Set(v8_str("root_object"), objects_[0]);
919   }
~GraphWithImplicitRefs()920   ~GraphWithImplicitRefs() {
921     instance_ = NULL;
922   }
923 
gcPrologue()924   static void gcPrologue() {
925     instance_->AddImplicitReferences();
926   }
927 
928  private:
AddImplicitReferences()929   void AddImplicitReferences() {
930     // 0 -> 1
931     v8::V8::AddImplicitReferences(
932         v8::Persistent<v8::Object>::Cast(objects_[0]), &objects_[1], 1);
933     // Adding two more references(note length=2 in params): 1 -> 2, 1 -> 3
934     v8::V8::AddImplicitReferences(
935         v8::Persistent<v8::Object>::Cast(objects_[1]), &objects_[2], 2);
936   }
937 
938   v8::Persistent<v8::Value> objects_[kObjectsCount];
939   static GraphWithImplicitRefs* instance_;
940 };
941 
942 GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL;
943 
944 
TEST(HeapSnapshotImplicitReferences)945 TEST(HeapSnapshotImplicitReferences) {
946   v8::HandleScope scope;
947   LocalContext env;
948 
949   GraphWithImplicitRefs graph(&env);
950   v8::V8::SetGlobalGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
951 
952   const v8::HeapSnapshot* snapshot =
953       v8::HeapProfiler::TakeSnapshot(v8_str("implicit_refs"));
954 
955   const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot);
956   // Use kShortcut type to skip intermediate JSGlobalPropertyCell
957   const v8::HeapGraphNode* obj0 = GetProperty(
958       global_object, v8::HeapGraphEdge::kShortcut, "root_object");
959   CHECK(obj0);
960   CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType());
961   const v8::HeapGraphNode* obj1 = GetProperty(
962       obj0, v8::HeapGraphEdge::kInternal, "native");
963   CHECK(obj1);
964   int implicit_targets_count = 0;
965   for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) {
966     const v8::HeapGraphEdge* prop = obj1->GetChild(i);
967     v8::String::AsciiValue prop_name(prop->GetName());
968     if (prop->GetType() == v8::HeapGraphEdge::kInternal &&
969         strcmp("native", *prop_name) == 0) {
970       ++implicit_targets_count;
971     }
972   }
973   CHECK_EQ(2, implicit_targets_count);
974   v8::V8::SetGlobalGCPrologueCallback(NULL);
975 }
976 
977 
TEST(DeleteAllHeapSnapshots)978 TEST(DeleteAllHeapSnapshots) {
979   v8::HandleScope scope;
980   LocalContext env;
981 
982   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
983   v8::HeapProfiler::DeleteAllSnapshots();
984   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
985   CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1")));
986   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
987   v8::HeapProfiler::DeleteAllSnapshots();
988   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
989   CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1")));
990   CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("2")));
991   CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
992   v8::HeapProfiler::DeleteAllSnapshots();
993   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
994 }
995 
996 
TEST(DeleteHeapSnapshot)997 TEST(DeleteHeapSnapshot) {
998   v8::HandleScope scope;
999   LocalContext env;
1000 
1001   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1002   const v8::HeapSnapshot* s1 =
1003       v8::HeapProfiler::TakeSnapshot(v8_str("1"));
1004   CHECK_NE(NULL, s1);
1005   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1006   unsigned uid1 = s1->GetUid();
1007   CHECK_EQ(s1, v8::HeapProfiler::FindSnapshot(uid1));
1008   const_cast<v8::HeapSnapshot*>(s1)->Delete();
1009   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1010   CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid1));
1011 
1012   const v8::HeapSnapshot* s2 =
1013       v8::HeapProfiler::TakeSnapshot(v8_str("2"));
1014   CHECK_NE(NULL, s2);
1015   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1016   unsigned uid2 = s2->GetUid();
1017   CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
1018   CHECK_EQ(s2, v8::HeapProfiler::FindSnapshot(uid2));
1019   const v8::HeapSnapshot* s3 =
1020       v8::HeapProfiler::TakeSnapshot(v8_str("3"));
1021   CHECK_NE(NULL, s3);
1022   CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
1023   unsigned uid3 = s3->GetUid();
1024   CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
1025   CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
1026   const_cast<v8::HeapSnapshot*>(s2)->Delete();
1027   CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1028   CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid2));
1029   CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
1030   const_cast<v8::HeapSnapshot*>(s3)->Delete();
1031   CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1032   CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid3));
1033 }
1034 
1035 
TEST(DocumentURL)1036 TEST(DocumentURL) {
1037   v8::HandleScope scope;
1038   LocalContext env;
1039 
1040   CompileRun("document = { URL:\"abcdefgh\" };");
1041 
1042   const v8::HeapSnapshot* snapshot =
1043       v8::HeapProfiler::TakeSnapshot(v8_str("document"));
1044   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1045   CHECK_NE(NULL, global);
1046   CHECK_EQ("Object / abcdefgh",
1047            const_cast<i::HeapEntry*>(
1048                reinterpret_cast<const i::HeapEntry*>(global))->name());
1049 }
1050 
1051 
TEST(DocumentWithException)1052 TEST(DocumentWithException) {
1053   v8::HandleScope scope;
1054   LocalContext env;
1055 
1056   CompileRun(
1057       "this.__defineGetter__(\"document\", function() { throw new Error(); })");
1058   const v8::HeapSnapshot* snapshot =
1059       v8::HeapProfiler::TakeSnapshot(v8_str("document"));
1060   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1061   CHECK_NE(NULL, global);
1062   CHECK_EQ("Object",
1063            const_cast<i::HeapEntry*>(
1064                reinterpret_cast<const i::HeapEntry*>(global))->name());
1065 }
1066 
1067 
TEST(DocumentURLWithException)1068 TEST(DocumentURLWithException) {
1069   v8::HandleScope scope;
1070   LocalContext env;
1071 
1072   CompileRun(
1073       "function URLWithException() {}\n"
1074       "URLWithException.prototype = { get URL() { throw new Error(); } };\n"
1075       "document = { URL: new URLWithException() };");
1076   const v8::HeapSnapshot* snapshot =
1077       v8::HeapProfiler::TakeSnapshot(v8_str("document"));
1078   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1079   CHECK_NE(NULL, global);
1080   CHECK_EQ("Object",
1081            const_cast<i::HeapEntry*>(
1082                reinterpret_cast<const i::HeapEntry*>(global))->name());
1083 }
1084 
1085 
TEST(NoHandleLeaks)1086 TEST(NoHandleLeaks) {
1087   v8::HandleScope scope;
1088   LocalContext env;
1089 
1090   CompileRun("document = { URL:\"abcdefgh\" };");
1091 
1092   v8::Handle<v8::String> name(v8_str("leakz"));
1093   int count_before = i::HandleScope::NumberOfHandles();
1094   v8::HeapProfiler::TakeSnapshot(name);
1095   int count_after = i::HandleScope::NumberOfHandles();
1096   CHECK_EQ(count_before, count_after);
1097 }
1098 
1099 
TEST(NodesIteration)1100 TEST(NodesIteration) {
1101   v8::HandleScope scope;
1102   LocalContext env;
1103   const v8::HeapSnapshot* snapshot =
1104       v8::HeapProfiler::TakeSnapshot(v8_str("iteration"));
1105   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1106   CHECK_NE(NULL, global);
1107   // Verify that we can find this object by iteration.
1108   const int nodes_count = snapshot->GetNodesCount();
1109   int count = 0;
1110   for (int i = 0; i < nodes_count; ++i) {
1111     if (snapshot->GetNode(i) == global)
1112       ++count;
1113   }
1114   CHECK_EQ(1, count);
1115 }
1116 
1117 
TEST(GetHeapValue)1118 TEST(GetHeapValue) {
1119   v8::HandleScope scope;
1120   LocalContext env;
1121 
1122   CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };");
1123   const v8::HeapSnapshot* snapshot =
1124       v8::HeapProfiler::TakeSnapshot(v8_str("value"));
1125   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1126   CHECK(global->GetHeapValue()->IsObject());
1127   v8::Local<v8::Object> js_global =
1128       env->Global()->GetPrototype().As<v8::Object>();
1129   CHECK(js_global == global->GetHeapValue());
1130   const v8::HeapGraphNode* obj = GetProperty(
1131       global, v8::HeapGraphEdge::kShortcut, "a");
1132   CHECK(obj->GetHeapValue()->IsObject());
1133   v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>();
1134   CHECK(js_obj == obj->GetHeapValue());
1135   const v8::HeapGraphNode* s_prop =
1136       GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
1137   v8::Local<v8::String> js_s_prop =
1138       js_obj->Get(v8_str("s_prop")).As<v8::String>();
1139   CHECK(js_s_prop == s_prop->GetHeapValue());
1140   const v8::HeapGraphNode* n_prop =
1141       GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
1142   v8::Local<v8::Number> js_n_prop =
1143       js_obj->Get(v8_str("n_prop")).As<v8::Number>();
1144   CHECK(js_n_prop == n_prop->GetHeapValue());
1145 }
1146 
1147 
TEST(GetHeapValueForDeletedObject)1148 TEST(GetHeapValueForDeletedObject) {
1149   v8::HandleScope scope;
1150   LocalContext env;
1151 
1152   // It is impossible to delete a global property, so we are about to delete a
1153   // property of the "a" object. Also, the "p" object can't be an empty one
1154   // because the empty object is static and isn't actually deleted.
1155   CompileRun("a = { p: { r: {} } };");
1156   const v8::HeapSnapshot* snapshot =
1157       v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
1158   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1159   const v8::HeapGraphNode* obj = GetProperty(
1160       global, v8::HeapGraphEdge::kShortcut, "a");
1161   const v8::HeapGraphNode* prop = GetProperty(
1162       obj, v8::HeapGraphEdge::kProperty, "p");
1163   {
1164     // Perform the check inside a nested local scope to avoid creating a
1165     // reference to the object we are deleting.
1166     v8::HandleScope scope;
1167     CHECK(prop->GetHeapValue()->IsObject());
1168   }
1169   CompileRun("delete a.p;");
1170   CHECK(prop->GetHeapValue()->IsUndefined());
1171 }
1172 
1173 
StringCmp(const char * ref,i::String * act)1174 static int StringCmp(const char* ref, i::String* act) {
1175   i::SmartArrayPointer<char> s_act = act->ToCString();
1176   int result = strcmp(ref, *s_act);
1177   if (result != 0)
1178     fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, *s_act);
1179   return result;
1180 }
1181 
1182 
TEST(GetConstructorName)1183 TEST(GetConstructorName) {
1184   v8::HandleScope scope;
1185   LocalContext env;
1186 
1187   CompileRun(
1188       "function Constructor1() {};\n"
1189       "var obj1 = new Constructor1();\n"
1190       "var Constructor2 = function() {};\n"
1191       "var obj2 = new Constructor2();\n"
1192       "var obj3 = {};\n"
1193       "obj3.constructor = function Constructor3() {};\n"
1194       "var obj4 = {};\n"
1195       "// Slow properties\n"
1196       "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1197       "obj4.constructor = function Constructor4() {};\n"
1198       "var obj5 = {};\n"
1199       "var obj6 = {};\n"
1200       "obj6.constructor = 6;");
1201   v8::Local<v8::Object> js_global =
1202       env->Global()->GetPrototype().As<v8::Object>();
1203   v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>();
1204   i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1);
1205   CHECK_EQ(0, StringCmp(
1206       "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
1207   v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>();
1208   i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2);
1209   CHECK_EQ(0, StringCmp(
1210       "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
1211   v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>();
1212   i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3);
1213   CHECK_EQ(0, StringCmp(
1214       "Constructor3", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
1215   v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>();
1216   i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4);
1217   CHECK_EQ(0, StringCmp(
1218       "Constructor4", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
1219   v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>();
1220   i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5);
1221   CHECK_EQ(0, StringCmp(
1222       "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
1223   v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>();
1224   i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6);
1225   CHECK_EQ(0, StringCmp(
1226       "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
1227 }
1228 
1229 
TEST(FastCaseGetter)1230 TEST(FastCaseGetter) {
1231   v8::HandleScope scope;
1232   LocalContext env;
1233 
1234   CompileRun("var obj1 = {};\n"
1235              "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1236              "  return 42;\n"
1237              "});\n"
1238              "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1239              "  return this.value_ = value;\n"
1240              "});\n");
1241   const v8::HeapSnapshot* snapshot =
1242       v8::HeapProfiler::TakeSnapshot(v8_str("fastCaseGetter"));
1243 
1244   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1245   CHECK_NE(NULL, global);
1246   const v8::HeapGraphNode* obj1 =
1247       GetProperty(global, v8::HeapGraphEdge::kShortcut, "obj1");
1248   CHECK_NE(NULL, obj1);
1249   const v8::HeapGraphNode* getterFunction =
1250       GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get-propWithGetter");
1251   CHECK_NE(NULL, getterFunction);
1252   const v8::HeapGraphNode* setterFunction =
1253       GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set-propWithSetter");
1254   CHECK_NE(NULL, setterFunction);
1255 }
1256 
1257 
HasWeakEdge(const v8::HeapGraphNode * node)1258 bool HasWeakEdge(const v8::HeapGraphNode* node) {
1259   for (int i = 0; i < node->GetChildrenCount(); ++i) {
1260     const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
1261     if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
1262   }
1263   return false;
1264 }
1265 
1266 
HasWeakGlobalHandle()1267 bool HasWeakGlobalHandle() {
1268   const v8::HeapSnapshot* snapshot =
1269       v8::HeapProfiler::TakeSnapshot(v8_str("weaks"));
1270   const v8::HeapGraphNode* gc_roots = GetNode(
1271       snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
1272   CHECK_NE(NULL, gc_roots);
1273   const v8::HeapGraphNode* global_handles = GetNode(
1274       gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
1275   CHECK_NE(NULL, global_handles);
1276   return HasWeakEdge(global_handles);
1277 }
1278 
1279 
PersistentHandleCallback(v8::Persistent<v8::Value> handle,void *)1280 static void PersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
1281   handle.Dispose();
1282 }
1283 
1284 
TEST(WeakGlobalHandle)1285 TEST(WeakGlobalHandle) {
1286   v8::HandleScope scope;
1287   LocalContext env;
1288 
1289   CHECK(!HasWeakGlobalHandle());
1290 
1291   v8::Persistent<v8::Object> handle =
1292       v8::Persistent<v8::Object>::New(v8::Object::New());
1293   handle.MakeWeak(NULL, PersistentHandleCallback);
1294 
1295   CHECK(HasWeakGlobalHandle());
1296 }
1297 
1298 
TEST(WeakGlobalContextRefs)1299 TEST(WeakGlobalContextRefs) {
1300   v8::HandleScope scope;
1301   LocalContext env;
1302 
1303   const v8::HeapSnapshot* snapshot =
1304       v8::HeapProfiler::TakeSnapshot(v8_str("weaks"));
1305   const v8::HeapGraphNode* gc_roots = GetNode(
1306       snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
1307   CHECK_NE(NULL, gc_roots);
1308   const v8::HeapGraphNode* global_handles = GetNode(
1309       gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
1310   CHECK_NE(NULL, global_handles);
1311   const v8::HeapGraphNode* global_context = GetNode(
1312       global_handles, v8::HeapGraphNode::kHidden, "system / GlobalContext");
1313   CHECK_NE(NULL, global_context);
1314   CHECK(HasWeakEdge(global_context));
1315 }
1316 
1317 
TEST(SfiAndJsFunctionWeakRefs)1318 TEST(SfiAndJsFunctionWeakRefs) {
1319   v8::HandleScope scope;
1320   LocalContext env;
1321 
1322   CompileRun(
1323       "fun = (function (x) { return function () { return x + 1; } })(1);");
1324   const v8::HeapSnapshot* snapshot =
1325       v8::HeapProfiler::TakeSnapshot(v8_str("fun"));
1326   const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1327   CHECK_NE(NULL, global);
1328   const v8::HeapGraphNode* fun =
1329       GetProperty(global, v8::HeapGraphEdge::kShortcut, "fun");
1330   CHECK(HasWeakEdge(fun));
1331   const v8::HeapGraphNode* shared =
1332       GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
1333   CHECK(HasWeakEdge(shared));
1334 }
1335 
1336 
TEST(PersistentHandleCount)1337 TEST(PersistentHandleCount) {
1338   v8::HandleScope scope;
1339   LocalContext env;
1340 
1341   // V8 also uses global handles internally, so we can't test for an absolute
1342   // number.
1343   int global_handle_count = v8::HeapProfiler::GetPersistentHandleCount();
1344 
1345   // Create some persistent handles.
1346   v8::Persistent<v8::String> p_AAA =
1347       v8::Persistent<v8::String>::New(v8_str("AAA"));
1348   CHECK_EQ(global_handle_count + 1,
1349            v8::HeapProfiler::GetPersistentHandleCount());
1350   v8::Persistent<v8::String> p_BBB =
1351       v8::Persistent<v8::String>::New(v8_str("BBB"));
1352   CHECK_EQ(global_handle_count + 2,
1353            v8::HeapProfiler::GetPersistentHandleCount());
1354   v8::Persistent<v8::String> p_CCC =
1355       v8::Persistent<v8::String>::New(v8_str("CCC"));
1356   CHECK_EQ(global_handle_count + 3,
1357            v8::HeapProfiler::GetPersistentHandleCount());
1358 
1359   // Dipose the persistent handles in a different order.
1360   p_AAA.Dispose();
1361   CHECK_EQ(global_handle_count + 2,
1362            v8::HeapProfiler::GetPersistentHandleCount());
1363   p_CCC.Dispose();
1364   CHECK_EQ(global_handle_count + 1,
1365            v8::HeapProfiler::GetPersistentHandleCount());
1366   p_BBB.Dispose();
1367   CHECK_EQ(global_handle_count, v8::HeapProfiler::GetPersistentHandleCount());
1368 }
1369