blob: 95314d74a4157c40904447df60eab5b18bc9834b [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2009 the V8 project authors. All rights reserved.
2//
3// Tests for heap profiler
4
5#ifdef ENABLE_LOGGING_AND_PROFILING
6
7#include "v8.h"
8#include "heap-profiler.h"
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009#include "snapshot.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000010#include "string-stream.h"
11#include "cctest.h"
Steve Block6ded16b2010-05-10 14:33:55 +010012#include "zone-inl.h"
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010013#include "../include/v8-profiler.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000014
15namespace i = v8::internal;
16using i::ClustersCoarser;
17using i::JSObjectsCluster;
18using i::JSObjectsRetainerTree;
19using i::JSObjectsClusterTree;
20using i::RetainerHeapProfile;
21
22
Steve Blocka7e24c12009-10-30 11:49:00 +000023namespace {
24
25class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile {
26 public:
27 ConstructorHeapProfileTestHelper()
28 : i::ConstructorHeapProfile(),
29 f_name_(i::Factory::NewStringFromAscii(i::CStrVector("F"))),
30 f_count_(0) {
31 }
32
33 void Call(const JSObjectsCluster& cluster,
34 const i::NumberAndSizeInfo& number_and_size) {
35 if (f_name_->Equals(cluster.constructor())) {
36 CHECK_EQ(f_count_, 0);
37 f_count_ = number_and_size.number();
38 CHECK_GT(f_count_, 0);
39 }
40 }
41
42 int f_count() { return f_count_; }
43
44 private:
45 i::Handle<i::String> f_name_;
46 int f_count_;
47};
48
49} // namespace
50
51
52TEST(ConstructorProfile) {
53 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +010054 LocalContext env;
Steve Blocka7e24c12009-10-30 11:49:00 +000055
Ben Murdochf87a2032010-10-22 12:50:53 +010056 CompileRun(
Steve Blocka7e24c12009-10-30 11:49:00 +000057 "function F() {} // A constructor\n"
58 "var f1 = new F();\n"
59 "var f2 = new F();\n");
60
61 ConstructorHeapProfileTestHelper cons_profile;
62 i::AssertNoAllocation no_alloc;
63 i::HeapIterator iterator;
Leon Clarked91b9f72010-01-27 17:25:45 +000064 for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next())
Steve Blocka7e24c12009-10-30 11:49:00 +000065 cons_profile.CollectStats(obj);
Steve Blocka7e24c12009-10-30 11:49:00 +000066 CHECK_EQ(0, cons_profile.f_count());
67 cons_profile.PrintStats();
68 CHECK_EQ(2, cons_profile.f_count());
69}
70
71
72static JSObjectsCluster AddHeapObjectToTree(JSObjectsRetainerTree* tree,
73 i::String* constructor,
74 int instance,
75 JSObjectsCluster* ref1 = NULL,
76 JSObjectsCluster* ref2 = NULL,
77 JSObjectsCluster* ref3 = NULL) {
78 JSObjectsCluster o(constructor, reinterpret_cast<i::Object*>(instance));
79 JSObjectsClusterTree* o_tree = new JSObjectsClusterTree();
80 JSObjectsClusterTree::Locator o_loc;
81 if (ref1 != NULL) o_tree->Insert(*ref1, &o_loc);
82 if (ref2 != NULL) o_tree->Insert(*ref2, &o_loc);
83 if (ref3 != NULL) o_tree->Insert(*ref3, &o_loc);
84 JSObjectsRetainerTree::Locator loc;
85 tree->Insert(o, &loc);
86 loc.set_value(o_tree);
87 return o;
88}
89
90
91static void AddSelfReferenceToTree(JSObjectsRetainerTree* tree,
92 JSObjectsCluster* self_ref) {
93 JSObjectsRetainerTree::Locator loc;
94 CHECK(tree->Find(*self_ref, &loc));
95 JSObjectsClusterTree::Locator o_loc;
96 CHECK_NE(NULL, loc.value());
97 loc.value()->Insert(*self_ref, &o_loc);
98}
99
100
101static inline void CheckEqualsHelper(const char* file, int line,
102 const char* expected_source,
103 const JSObjectsCluster& expected,
104 const char* value_source,
105 const JSObjectsCluster& value) {
106 if (JSObjectsCluster::Compare(expected, value) != 0) {
107 i::HeapStringAllocator allocator;
108 i::StringStream stream(&allocator);
109 stream.Add("# Expected: ");
110 expected.DebugPrint(&stream);
111 stream.Add("\n# Found: ");
112 value.DebugPrint(&stream);
113 V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n%s",
114 expected_source, value_source,
115 *stream.ToCString());
116 }
117}
118
119
120static inline void CheckNonEqualsHelper(const char* file, int line,
121 const char* expected_source,
122 const JSObjectsCluster& expected,
123 const char* value_source,
124 const JSObjectsCluster& value) {
125 if (JSObjectsCluster::Compare(expected, value) == 0) {
126 i::HeapStringAllocator allocator;
127 i::StringStream stream(&allocator);
128 stream.Add("# !Expected: ");
129 expected.DebugPrint(&stream);
130 stream.Add("\n# Found: ");
131 value.DebugPrint(&stream);
132 V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n%s",
133 expected_source, value_source,
134 *stream.ToCString());
135 }
136}
137
138
139TEST(ClustersCoarserSimple) {
140 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100141 LocalContext env;
Steve Blocka7e24c12009-10-30 11:49:00 +0000142
143 i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
144
145 JSObjectsRetainerTree tree;
146 JSObjectsCluster function(i::Heap::function_class_symbol());
147 JSObjectsCluster a(*i::Factory::NewStringFromAscii(i::CStrVector("A")));
148 JSObjectsCluster b(*i::Factory::NewStringFromAscii(i::CStrVector("B")));
149
150 // o1 <- Function
151 JSObjectsCluster o1 =
152 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100, &function);
153 // o2 <- Function
154 JSObjectsCluster o2 =
155 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x200, &function);
156 // o3 <- A, B
157 JSObjectsCluster o3 =
158 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &a, &b);
159 // o4 <- B, A
160 JSObjectsCluster o4 =
161 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x400, &b, &a);
162 // o5 <- A, B, Function
163 JSObjectsCluster o5 =
164 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x500,
165 &a, &b, &function);
166
167 ClustersCoarser coarser;
168 coarser.Process(&tree);
169
170 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2));
171 CHECK_EQ(coarser.GetCoarseEquivalent(o3), coarser.GetCoarseEquivalent(o4));
172 CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o3));
173 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o5));
174}
175
176
177TEST(ClustersCoarserMultipleConstructors) {
178 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100179 LocalContext env;
Steve Blocka7e24c12009-10-30 11:49:00 +0000180
181 i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
182
183 JSObjectsRetainerTree tree;
184 JSObjectsCluster function(i::Heap::function_class_symbol());
185
186 // o1 <- Function
187 JSObjectsCluster o1 =
188 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100, &function);
189 // a1 <- Function
190 JSObjectsCluster a1 =
191 AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x1000, &function);
192 // o2 <- Function
193 JSObjectsCluster o2 =
194 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x200, &function);
195 // a2 <- Function
196 JSObjectsCluster a2 =
197 AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x2000, &function);
198
199 ClustersCoarser coarser;
200 coarser.Process(&tree);
201
202 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2));
203 CHECK_EQ(coarser.GetCoarseEquivalent(a1), coarser.GetCoarseEquivalent(a2));
204}
205
206
207TEST(ClustersCoarserPathsTraversal) {
208 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100209 LocalContext env;
Steve Blocka7e24c12009-10-30 11:49:00 +0000210
211 i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
212
213 JSObjectsRetainerTree tree;
214
215 // On the following graph:
216 //
217 // p
218 // <- o21 <- o11 <-
219 // q o
220 // <- o22 <- o12 <-
221 // r
222 //
223 // we expect that coarser will deduce equivalences: p ~ q ~ r,
224 // o21 ~ o22, and o11 ~ o12.
225
226 JSObjectsCluster o =
227 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100);
228 JSObjectsCluster o11 =
229 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x110, &o);
230 JSObjectsCluster o12 =
231 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x120, &o);
232 JSObjectsCluster o21 =
233 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x210, &o11);
234 JSObjectsCluster o22 =
235 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x220, &o12);
236 JSObjectsCluster p =
237 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &o21);
238 JSObjectsCluster q =
239 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x310, &o21, &o22);
240 JSObjectsCluster r =
241 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x320, &o22);
242
243 ClustersCoarser coarser;
244 coarser.Process(&tree);
245
246 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o));
247 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o11));
248 CHECK_EQ(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o12));
249 CHECK_EQ(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(o22));
250 CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o21));
251 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p));
252 CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q));
253 CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r));
254 CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(p));
255 CHECK_NE(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(p));
256}
257
258
259TEST(ClustersCoarserSelf) {
260 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100261 LocalContext env;
Steve Blocka7e24c12009-10-30 11:49:00 +0000262
263 i::ZoneScope zn_scope(i::DELETE_ON_EXIT);
264
265 JSObjectsRetainerTree tree;
266
267 // On the following graph:
268 //
269 // p (self-referencing)
270 // <- o1 <-
271 // q (self-referencing) o
272 // <- o2 <-
273 // r (self-referencing)
274 //
275 // we expect that coarser will deduce equivalences: p ~ q ~ r, o1 ~ o2;
276
277 JSObjectsCluster o =
278 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100);
279 JSObjectsCluster o1 =
280 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x110, &o);
281 JSObjectsCluster o2 =
282 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x120, &o);
283 JSObjectsCluster p =
284 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &o1);
285 AddSelfReferenceToTree(&tree, &p);
286 JSObjectsCluster q =
287 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x310, &o1, &o2);
288 AddSelfReferenceToTree(&tree, &q);
289 JSObjectsCluster r =
290 AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x320, &o2);
291 AddSelfReferenceToTree(&tree, &r);
292
293 ClustersCoarser coarser;
294 coarser.Process(&tree);
295
296 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o));
297 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o1));
298 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2));
299 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p));
300 CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q));
301 CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r));
302 CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(p));
303}
304
305
306namespace {
307
308class RetainerProfilePrinter : public RetainerHeapProfile::Printer {
309 public:
310 RetainerProfilePrinter() : stream_(&allocator_), lines_(100) {}
311
312 void PrintRetainers(const JSObjectsCluster& cluster,
313 const i::StringStream& retainers) {
314 cluster.Print(&stream_);
315 stream_.Add("%s", *(retainers.ToCString()));
316 stream_.Put('\0');
317 }
318
319 const char* GetRetainers(const char* constructor) {
320 FillLines();
321 const size_t cons_len = strlen(constructor);
322 for (int i = 0; i < lines_.length(); ++i) {
323 if (strncmp(constructor, lines_[i], cons_len) == 0 &&
324 lines_[i][cons_len] == ',') {
325 return lines_[i] + cons_len + 1;
326 }
327 }
328 return NULL;
329 }
330
331 private:
332 void FillLines() {
333 if (lines_.length() > 0) return;
334 stream_.Put('\0');
335 stream_str_ = stream_.ToCString();
336 const char* pos = *stream_str_;
337 while (pos != NULL && *pos != '\0') {
338 lines_.Add(pos);
339 pos = strchr(pos, '\0');
340 if (pos != NULL) ++pos;
341 }
342 }
343
344 i::HeapStringAllocator allocator_;
345 i::StringStream stream_;
346 i::SmartPointer<const char> stream_str_;
347 i::List<const char*> lines_;
348};
349
350} // namespace
351
352
353TEST(RetainerProfile) {
354 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100355 LocalContext env;
Steve Blocka7e24c12009-10-30 11:49:00 +0000356
Ben Murdochf87a2032010-10-22 12:50:53 +0100357 CompileRun(
Steve Blocka7e24c12009-10-30 11:49:00 +0000358 "function A() {}\n"
359 "function B(x) { this.x = x; }\n"
360 "function C(x) { this.x1 = x; this.x2 = x; }\n"
361 "var a = new A();\n"
362 "var b1 = new B(a), b2 = new B(a);\n"
363 "var c = new C(a);");
364
365 RetainerHeapProfile ret_profile;
366 i::AssertNoAllocation no_alloc;
367 i::HeapIterator iterator;
Leon Clarked91b9f72010-01-27 17:25:45 +0000368 for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next())
Steve Blocka7e24c12009-10-30 11:49:00 +0000369 ret_profile.CollectStats(obj);
Steve Block791712a2010-08-27 10:21:07 +0100370 ret_profile.CoarseAndAggregate();
Steve Blocka7e24c12009-10-30 11:49:00 +0000371 RetainerProfilePrinter printer;
372 ret_profile.DebugPrintStats(&printer);
373 const char* retainers_of_a = printer.GetRetainers("A");
374 // The order of retainers is unspecified, so we check string length, and
375 // verify each retainer separately.
Steve Blockd0582a62009-12-15 09:54:21 +0000376 CHECK_EQ(i::StrLength("(global property);1,B;2,C;2"),
377 i::StrLength(retainers_of_a));
Steve Blocka7e24c12009-10-30 11:49:00 +0000378 CHECK(strstr(retainers_of_a, "(global property);1") != NULL);
379 CHECK(strstr(retainers_of_a, "B;2") != NULL);
380 CHECK(strstr(retainers_of_a, "C;2") != NULL);
381 CHECK_EQ("(global property);2", printer.GetRetainers("B"));
382 CHECK_EQ("(global property);1", printer.GetRetainers("C"));
383}
384
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100385
386namespace {
387
388class NamedEntriesDetector {
389 public:
390 NamedEntriesDetector()
Russell Brenner90bac252010-11-18 13:33:46 -0800391 : has_A2(false), has_B2(false), has_C2(false) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100392 }
393
Iain Merrick75681382010-08-19 15:07:18 +0100394 void Apply(i::HeapEntry** entry_ptr) {
Iain Merrick75681382010-08-19 15:07:18 +0100395 if (IsReachableNodeWithName(*entry_ptr, "A2")) has_A2 = true;
396 if (IsReachableNodeWithName(*entry_ptr, "B2")) has_B2 = true;
397 if (IsReachableNodeWithName(*entry_ptr, "C2")) has_C2 = true;
398 }
399
400 static bool IsReachableNodeWithName(i::HeapEntry* entry, const char* name) {
401 return strcmp(name, entry->name()) == 0 && entry->painted_reachable();
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100402 }
403
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100404 bool has_A2;
405 bool has_B2;
406 bool has_C2;
407};
408
409} // namespace
410
411
412static const v8::HeapGraphNode* GetGlobalObject(
413 const v8::HeapSnapshot* snapshot) {
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800414 CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount());
415 const v8::HeapGraphNode* global_obj =
416 snapshot->GetRoot()->GetChild(0)->GetToNode();
417 CHECK_EQ("Object", const_cast<i::HeapEntry*>(
418 reinterpret_cast<const i::HeapEntry*>(global_obj))->name());
419 return global_obj;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100420}
421
422
423static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
424 v8::HeapGraphEdge::Type type,
425 const char* name) {
426 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
427 const v8::HeapGraphEdge* prop = node->GetChild(i);
428 v8::String::AsciiValue prop_name(prop->GetName());
429 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
430 return prop->GetToNode();
431 }
432 return NULL;
433}
434
435
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100436static bool IsNodeRetainedAs(const v8::HeapGraphNode* node,
437 v8::HeapGraphEdge::Type type,
438 const char* name) {
439 for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) {
440 const v8::HeapGraphEdge* prop = node->GetRetainer(i);
441 v8::String::AsciiValue prop_name(prop->GetName());
442 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
443 return true;
444 }
445 return false;
446}
447
448
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100449static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
450 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
451 const v8::HeapGraphEdge* prop = node->GetChild(i);
452 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100453 if (node->GetType() == v8::HeapGraphNode::kString) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100454 v8::String::AsciiValue node_name(node->GetName());
455 if (strcmp(contents, *node_name) == 0) return true;
456 }
457 }
458 return false;
459}
460
461
462TEST(HeapSnapshot) {
463 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100464 LocalContext env2;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100465
Ben Murdochf87a2032010-10-22 12:50:53 +0100466 CompileRun(
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100467 "function A2() {}\n"
468 "function B2(x) { return function() { return typeof x; }; }\n"
469 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
470 "var a2 = new A2();\n"
471 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
472 "var c2 = new C2(a2);");
473 const v8::HeapSnapshot* snapshot_env2 =
474 v8::HeapProfiler::TakeSnapshot(v8::String::New("env2"));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100475 i::HeapSnapshot* i_snapshot_env2 =
476 const_cast<i::HeapSnapshot*>(
477 reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
Iain Merrick75681382010-08-19 15:07:18 +0100478 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
479 // Paint all nodes reachable from global object.
480 i_snapshot_env2->ClearPaint();
481 const_cast<i::HeapEntry*>(
482 reinterpret_cast<const i::HeapEntry*>(global_env2))->PaintAllReachable();
483
Russell Brenner90bac252010-11-18 13:33:46 -0800484 // Verify, that JS global object of env2 has '..2' properties.
Iain Merrick75681382010-08-19 15:07:18 +0100485 const v8::HeapGraphNode* a2_node =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800486 GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "a2");
Iain Merrick75681382010-08-19 15:07:18 +0100487 CHECK_NE(NULL, a2_node);
488 CHECK_NE(
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800489 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1"));
Iain Merrick75681382010-08-19 15:07:18 +0100490 CHECK_NE(
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800491 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2"));
492 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2"));
Iain Merrick75681382010-08-19 15:07:18 +0100493
Iain Merrick75681382010-08-19 15:07:18 +0100494 NamedEntriesDetector det;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100495 i_snapshot_env2->IterateEntries(&det);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100496 CHECK(det.has_A2);
497 CHECK(det.has_B2);
498 CHECK(det.has_C2);
499
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800500 /*
501 // Currently disabled. Too many retaining paths emerge, need to
502 // reduce the amount.
503
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100504 // Verify 'a2' object retainers. They are:
505 // - (global object).a2
506 // - c2.x1, c2.x2, c2[1]
507 // - b2_1 and b2_2 closures: via 'x' variable
508 CHECK_EQ(6, a2_node->GetRetainingPathsCount());
509 bool has_global_obj_a2_ref = false;
510 bool has_c2_x1_ref = false, has_c2_x2_ref = false, has_c2_1_ref = false;
511 bool has_b2_1_x_ref = false, has_b2_2_x_ref = false;
512 for (int i = 0; i < a2_node->GetRetainingPathsCount(); ++i) {
513 const v8::HeapGraphPath* path = a2_node->GetRetainingPath(i);
514 const int edges_count = path->GetEdgesCount();
515 CHECK_GT(edges_count, 0);
516 const v8::HeapGraphEdge* last_edge = path->GetEdge(edges_count - 1);
517 v8::String::AsciiValue last_edge_name(last_edge->GetName());
518 if (strcmp("a2", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100519 && last_edge->GetType() == v8::HeapGraphEdge::kProperty) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100520 has_global_obj_a2_ref = true;
521 continue;
522 }
523 CHECK_GT(edges_count, 1);
524 const v8::HeapGraphEdge* prev_edge = path->GetEdge(edges_count - 2);
525 v8::String::AsciiValue prev_edge_name(prev_edge->GetName());
526 if (strcmp("x1", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100527 && last_edge->GetType() == v8::HeapGraphEdge::kProperty
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100528 && strcmp("c2", *prev_edge_name) == 0) has_c2_x1_ref = true;
529 if (strcmp("x2", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100530 && last_edge->GetType() == v8::HeapGraphEdge::kProperty
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100531 && strcmp("c2", *prev_edge_name) == 0) has_c2_x2_ref = true;
532 if (strcmp("1", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100533 && last_edge->GetType() == v8::HeapGraphEdge::kElement
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100534 && strcmp("c2", *prev_edge_name) == 0) has_c2_1_ref = true;
535 if (strcmp("x", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100536 && last_edge->GetType() == v8::HeapGraphEdge::kContextVariable
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100537 && strcmp("b2_1", *prev_edge_name) == 0) has_b2_1_x_ref = true;
538 if (strcmp("x", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100539 && last_edge->GetType() == v8::HeapGraphEdge::kContextVariable
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100540 && strcmp("b2_2", *prev_edge_name) == 0) has_b2_2_x_ref = true;
541 }
542 CHECK(has_global_obj_a2_ref);
543 CHECK(has_c2_x1_ref);
544 CHECK(has_c2_x2_ref);
545 CHECK(has_c2_1_ref);
546 CHECK(has_b2_1_x_ref);
547 CHECK(has_b2_2_x_ref);
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800548 */
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100549}
550
551
Iain Merrick75681382010-08-19 15:07:18 +0100552TEST(HeapSnapshotObjectSizes) {
553 v8::HandleScope scope;
554 LocalContext env;
555
556 // -a-> X1 --a
557 // x -b-> X2 <-|
Ben Murdochf87a2032010-10-22 12:50:53 +0100558 CompileRun(
Iain Merrick75681382010-08-19 15:07:18 +0100559 "function X(a, b) { this.a = a; this.b = b; }\n"
560 "x = new X(new X(), new X());\n"
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800561 "(function() { x.a.a = x.b; })();");
Iain Merrick75681382010-08-19 15:07:18 +0100562 const v8::HeapSnapshot* snapshot =
563 v8::HeapProfiler::TakeSnapshot(v8::String::New("sizes"));
564 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
565 const v8::HeapGraphNode* x =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800566 GetProperty(global, v8::HeapGraphEdge::kShortcut, "x");
Iain Merrick75681382010-08-19 15:07:18 +0100567 CHECK_NE(NULL, x);
Iain Merrick75681382010-08-19 15:07:18 +0100568 const v8::HeapGraphNode* x1 =
569 GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
570 CHECK_NE(NULL, x1);
571 const v8::HeapGraphNode* x2 =
572 GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
573 CHECK_NE(NULL, x2);
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800574
575 // Test approximate sizes.
576 CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(false));
577 CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(false));
578 CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(false));
579 // Test exact sizes.
580 CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(true));
581 CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(true));
582 CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(true));
Iain Merrick75681382010-08-19 15:07:18 +0100583}
584
585
586TEST(HeapSnapshotEntryChildren) {
587 v8::HandleScope scope;
588 LocalContext env;
589
Ben Murdochf87a2032010-10-22 12:50:53 +0100590 CompileRun(
Iain Merrick75681382010-08-19 15:07:18 +0100591 "function A() { }\n"
592 "a = new A;");
593 const v8::HeapSnapshot* snapshot =
594 v8::HeapProfiler::TakeSnapshot(v8::String::New("children"));
595 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
596 for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
597 const v8::HeapGraphEdge* prop = global->GetChild(i);
598 CHECK_EQ(global, prop->GetFromNode());
599 }
600 const v8::HeapGraphNode* a =
601 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
602 CHECK_NE(NULL, a);
603 for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
604 const v8::HeapGraphEdge* prop = a->GetChild(i);
605 CHECK_EQ(a, prop->GetFromNode());
606 }
607}
608
609
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100610TEST(HeapSnapshotCodeObjects) {
611 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100612 LocalContext env;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100613
Ben Murdochf87a2032010-10-22 12:50:53 +0100614 CompileRun(
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100615 "function lazy(x) { return x - 1; }\n"
616 "function compiled(x) { return x + 1; }\n"
Steve Block791712a2010-08-27 10:21:07 +0100617 "var anonymous = (function() { return function() { return 0; } })();\n"
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100618 "compiled(1)");
619 const v8::HeapSnapshot* snapshot =
620 v8::HeapProfiler::TakeSnapshot(v8::String::New("code"));
621
622 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
623 const v8::HeapGraphNode* compiled =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800624 GetProperty(global, v8::HeapGraphEdge::kShortcut, "compiled");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100625 CHECK_NE(NULL, compiled);
Iain Merrick75681382010-08-19 15:07:18 +0100626 CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100627 const v8::HeapGraphNode* lazy =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800628 GetProperty(global, v8::HeapGraphEdge::kShortcut, "lazy");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100629 CHECK_NE(NULL, lazy);
Iain Merrick75681382010-08-19 15:07:18 +0100630 CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
Steve Block791712a2010-08-27 10:21:07 +0100631 const v8::HeapGraphNode* anonymous =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800632 GetProperty(global, v8::HeapGraphEdge::kShortcut, "anonymous");
Steve Block791712a2010-08-27 10:21:07 +0100633 CHECK_NE(NULL, anonymous);
634 CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
635 v8::String::AsciiValue anonymous_name(anonymous->GetName());
Ben Murdochf87a2032010-10-22 12:50:53 +0100636 CHECK_EQ("", *anonymous_name);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100637
638 // Find references to code.
639 const v8::HeapGraphNode* compiled_code =
Iain Merrick75681382010-08-19 15:07:18 +0100640 GetProperty(compiled, v8::HeapGraphEdge::kInternal, "code");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100641 CHECK_NE(NULL, compiled_code);
642 const v8::HeapGraphNode* lazy_code =
Iain Merrick75681382010-08-19 15:07:18 +0100643 GetProperty(lazy, v8::HeapGraphEdge::kInternal, "code");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100644 CHECK_NE(NULL, lazy_code);
645
646 // Verify that non-compiled code doesn't contain references to "x"
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100647 // literal, while compiled code does. The scope info is stored in FixedArray
648 // objects attached to the SharedFunctionInfo.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100649 bool compiled_references_x = false, lazy_references_x = false;
650 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
651 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
652 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100653 if (node->GetType() == v8::HeapGraphNode::kArray) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100654 if (HasString(node, "x")) {
655 compiled_references_x = true;
656 break;
657 }
658 }
659 }
660 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
661 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
662 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100663 if (node->GetType() == v8::HeapGraphNode::kArray) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100664 if (HasString(node, "x")) {
665 lazy_references_x = true;
666 break;
667 }
668 }
669 }
670 CHECK(compiled_references_x);
671 CHECK(!lazy_references_x);
672}
673
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100674
Ben Murdochf87a2032010-10-22 12:50:53 +0100675TEST(HeapSnapshotHeapNumbers) {
676 v8::HandleScope scope;
677 LocalContext env;
678 CompileRun(
679 "a = 1; // a is Smi\n"
680 "b = 2.5; // b is HeapNumber");
681 const v8::HeapSnapshot* snapshot =
682 v8::HeapProfiler::TakeSnapshot(v8::String::New("numbers"));
683 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800684 CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kShortcut, "a"));
Ben Murdochf87a2032010-10-22 12:50:53 +0100685 const v8::HeapGraphNode* b =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800686 GetProperty(global, v8::HeapGraphEdge::kShortcut, "b");
Ben Murdochf87a2032010-10-22 12:50:53 +0100687 CHECK_NE(NULL, b);
688 CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
689}
690
691
692TEST(HeapSnapshotInternalReferences) {
693 v8::HandleScope scope;
694 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
695 global_template->SetInternalFieldCount(2);
696 LocalContext env(NULL, global_template);
697 v8::Handle<v8::Object> global_proxy = env->Global();
698 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
699 CHECK_EQ(2, global->InternalFieldCount());
700 v8::Local<v8::Object> obj = v8::Object::New();
701 global->SetInternalField(0, v8_num(17));
702 global->SetInternalField(1, obj);
703 const v8::HeapSnapshot* snapshot =
704 v8::HeapProfiler::TakeSnapshot(v8::String::New("internals"));
705 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
706 // The first reference will not present, because it's a Smi.
707 CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
708 // The second reference is to an object.
709 CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
710}
711
712
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100713// Trying to introduce a check helper for uint64_t causes many
714// overloading ambiguities, so it seems easier just to cast
715// them to a signed type.
716#define CHECK_EQ_UINT64_T(a, b) \
717 CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b))
Iain Merrick75681382010-08-19 15:07:18 +0100718#define CHECK_NE_UINT64_T(a, b) \
719 CHECK((a) != (b)) // NOLINT
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100720
721TEST(HeapEntryIdsAndGC) {
722 v8::HandleScope scope;
723 LocalContext env;
724
Ben Murdochf87a2032010-10-22 12:50:53 +0100725 CompileRun(
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100726 "function A() {}\n"
727 "function B(x) { this.x = x; }\n"
728 "var a = new A();\n"
729 "var b = new B(a);");
730 const v8::HeapSnapshot* snapshot1 =
731 v8::HeapProfiler::TakeSnapshot(v8::String::New("s1"));
732
733 i::Heap::CollectAllGarbage(true); // Enforce compaction.
734
735 const v8::HeapSnapshot* snapshot2 =
736 v8::HeapProfiler::TakeSnapshot(v8::String::New("s2"));
737
738 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
739 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
740 CHECK_NE_UINT64_T(0, global1->GetId());
741 CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
742 const v8::HeapGraphNode* A1 =
Iain Merrick75681382010-08-19 15:07:18 +0100743 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
744 CHECK_NE(NULL, A1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100745 const v8::HeapGraphNode* A2 =
Iain Merrick75681382010-08-19 15:07:18 +0100746 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
747 CHECK_NE(NULL, A2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100748 CHECK_NE_UINT64_T(0, A1->GetId());
749 CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId());
750 const v8::HeapGraphNode* B1 =
Iain Merrick75681382010-08-19 15:07:18 +0100751 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
752 CHECK_NE(NULL, B1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100753 const v8::HeapGraphNode* B2 =
Iain Merrick75681382010-08-19 15:07:18 +0100754 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
755 CHECK_NE(NULL, B2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100756 CHECK_NE_UINT64_T(0, B1->GetId());
757 CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId());
758 const v8::HeapGraphNode* a1 =
Iain Merrick75681382010-08-19 15:07:18 +0100759 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
760 CHECK_NE(NULL, a1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100761 const v8::HeapGraphNode* a2 =
Iain Merrick75681382010-08-19 15:07:18 +0100762 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
763 CHECK_NE(NULL, a2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100764 CHECK_NE_UINT64_T(0, a1->GetId());
765 CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
766 const v8::HeapGraphNode* b1 =
Iain Merrick75681382010-08-19 15:07:18 +0100767 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
768 CHECK_NE(NULL, b1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100769 const v8::HeapGraphNode* b2 =
Iain Merrick75681382010-08-19 15:07:18 +0100770 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
771 CHECK_NE(NULL, b2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100772 CHECK_NE_UINT64_T(0, b1->GetId());
773 CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId());
774}
775
776
777TEST(HeapSnapshotsDiff) {
778 v8::HandleScope scope;
779 LocalContext env;
780
Ben Murdochf87a2032010-10-22 12:50:53 +0100781 CompileRun(
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100782 "function A() {}\n"
783 "function B(x) { this.x = x; }\n"
Ben Murdochf87a2032010-10-22 12:50:53 +0100784 "function A2(a) { for (var i = 0; i < a; ++i) this[i] = i; }\n"
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100785 "var a = new A();\n"
786 "var b = new B(a);");
787 const v8::HeapSnapshot* snapshot1 =
788 v8::HeapProfiler::TakeSnapshot(v8::String::New("s1"));
789
Ben Murdochf87a2032010-10-22 12:50:53 +0100790 CompileRun(
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100791 "delete a;\n"
792 "b.x = null;\n"
Ben Murdochf87a2032010-10-22 12:50:53 +0100793 "var a = new A2(20);\n"
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100794 "var b2 = new B(a);");
795 const v8::HeapSnapshot* snapshot2 =
796 v8::HeapProfiler::TakeSnapshot(v8::String::New("s2"));
797
798 const v8::HeapSnapshotsDiff* diff = snapshot1->CompareWith(snapshot2);
799
800 // Verify additions: ensure that addition of A and B was detected.
801 const v8::HeapGraphNode* additions_root = diff->GetAdditionsRoot();
802 bool found_A = false, found_B = false;
803 uint64_t s1_A_id = 0;
804 for (int i = 0, count = additions_root->GetChildrenCount(); i < count; ++i) {
805 const v8::HeapGraphEdge* prop = additions_root->GetChild(i);
806 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100807 if (node->GetType() == v8::HeapGraphNode::kObject) {
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100808 v8::String::AsciiValue node_name(node->GetName());
Ben Murdochf87a2032010-10-22 12:50:53 +0100809 if (strcmp(*node_name, "A2") == 0) {
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800810 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "a"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100811 CHECK(!found_A);
812 found_A = true;
813 s1_A_id = node->GetId();
814 } else if (strcmp(*node_name, "B") == 0) {
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800815 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "b2"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100816 CHECK(!found_B);
817 found_B = true;
818 }
819 }
820 }
821 CHECK(found_A);
822 CHECK(found_B);
823
824 // Verify deletions: ensure that deletion of A was detected.
825 const v8::HeapGraphNode* deletions_root = diff->GetDeletionsRoot();
826 bool found_A_del = false;
827 uint64_t s2_A_id = 0;
828 for (int i = 0, count = deletions_root->GetChildrenCount(); i < count; ++i) {
829 const v8::HeapGraphEdge* prop = deletions_root->GetChild(i);
830 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100831 if (node->GetType() == v8::HeapGraphNode::kObject) {
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100832 v8::String::AsciiValue node_name(node->GetName());
833 if (strcmp(*node_name, "A") == 0) {
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800834 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "a"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100835 CHECK(!found_A_del);
836 found_A_del = true;
837 s2_A_id = node->GetId();
838 }
839 }
840 }
841 CHECK(found_A_del);
842 CHECK_NE_UINT64_T(0, s1_A_id);
843 CHECK(s1_A_id != s2_A_id);
844}
845
Iain Merrick75681382010-08-19 15:07:18 +0100846
Ben Murdochf87a2032010-10-22 12:50:53 +0100847TEST(HeapSnapshotRootPreservedAfterSorting) {
848 v8::HandleScope scope;
849 LocalContext env;
850 const v8::HeapSnapshot* snapshot =
851 v8::HeapProfiler::TakeSnapshot(v8::String::New("s"));
852 const v8::HeapGraphNode* root1 = snapshot->GetRoot();
853 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
854 snapshot))->GetSortedEntriesList();
855 const v8::HeapGraphNode* root2 = snapshot->GetRoot();
856 CHECK_EQ(root1, root2);
857}
858
859
Steve Block791712a2010-08-27 10:21:07 +0100860static const v8::HeapGraphNode* GetChild(
861 const v8::HeapGraphNode* node,
862 v8::HeapGraphNode::Type type,
863 const char* name,
864 const v8::HeapGraphNode* after = NULL) {
865 bool ignore_child = after == NULL ? false : true;
866 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
867 const v8::HeapGraphEdge* prop = node->GetChild(i);
868 const v8::HeapGraphNode* child = prop->GetToNode();
869 v8::String::AsciiValue child_name(child->GetName());
870 if (!ignore_child
871 && child->GetType() == type
872 && strcmp(name, *child_name) == 0)
873 return child;
874 if (after != NULL && child == after) ignore_child = false;
875 }
876 return NULL;
877}
878
879static bool IsNodeRetainedAs(const v8::HeapGraphNode* node,
880 int element) {
881 for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) {
882 const v8::HeapGraphEdge* prop = node->GetRetainer(i);
883 if (prop->GetType() == v8::HeapGraphEdge::kElement
884 && element == prop->GetName()->Int32Value())
885 return true;
886 }
887 return false;
888}
889
890TEST(AggregatedHeapSnapshot) {
891 v8::HandleScope scope;
892 LocalContext env;
893
Ben Murdochf87a2032010-10-22 12:50:53 +0100894 CompileRun(
Steve Block791712a2010-08-27 10:21:07 +0100895 "function A() {}\n"
896 "function B(x) { this.x = x; }\n"
897 "var a = new A();\n"
898 "var b = new B(a);");
899 const v8::HeapSnapshot* snapshot =
900 v8::HeapProfiler::TakeSnapshot(
901 v8::String::New("agg"), v8::HeapSnapshot::kAggregated);
902 const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(),
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800903 v8::HeapGraphNode::kHidden,
Steve Block791712a2010-08-27 10:21:07 +0100904 "STRING_TYPE");
905 CHECK_NE(NULL, strings);
906 CHECK_NE(0, strings->GetSelfSize());
907 CHECK_NE(0, strings->GetInstancesCount());
908 const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(),
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800909 v8::HeapGraphNode::kHidden,
Steve Block791712a2010-08-27 10:21:07 +0100910 "MAP_TYPE");
911 CHECK_NE(NULL, maps);
912 CHECK_NE(0, maps->GetSelfSize());
913 CHECK_NE(0, maps->GetInstancesCount());
914
915 const v8::HeapGraphNode* a = GetChild(snapshot->GetRoot(),
916 v8::HeapGraphNode::kObject,
917 "A");
918 CHECK_NE(NULL, a);
919 CHECK_NE(0, a->GetSelfSize());
920 CHECK_EQ(1, a->GetInstancesCount());
921
922 const v8::HeapGraphNode* b = GetChild(snapshot->GetRoot(),
923 v8::HeapGraphNode::kObject,
924 "B");
925 CHECK_NE(NULL, b);
926 CHECK_NE(0, b->GetSelfSize());
927 CHECK_EQ(1, b->GetInstancesCount());
928
929 const v8::HeapGraphNode* glob_prop = GetChild(snapshot->GetRoot(),
930 v8::HeapGraphNode::kObject,
931 "(global property)",
932 b);
933 CHECK_NE(NULL, glob_prop);
934 CHECK_EQ(0, glob_prop->GetSelfSize());
935 CHECK_EQ(0, glob_prop->GetInstancesCount());
936 CHECK_NE(0, glob_prop->GetChildrenCount());
937
938 const v8::HeapGraphNode* a_from_glob_prop = GetChild(
939 glob_prop,
940 v8::HeapGraphNode::kObject,
941 "A");
942 CHECK_NE(NULL, a_from_glob_prop);
943 CHECK_EQ(0, a_from_glob_prop->GetSelfSize());
944 CHECK_EQ(0, a_from_glob_prop->GetInstancesCount());
945 CHECK_EQ(0, a_from_glob_prop->GetChildrenCount()); // Retains nothing.
946 CHECK(IsNodeRetainedAs(a_from_glob_prop, 1)); // (global propery) has 1 ref.
947
948 const v8::HeapGraphNode* b_with_children = GetChild(
949 snapshot->GetRoot(),
950 v8::HeapGraphNode::kObject,
951 "B",
952 b);
953 CHECK_NE(NULL, b_with_children);
954 CHECK_EQ(0, b_with_children->GetSelfSize());
955 CHECK_EQ(0, b_with_children->GetInstancesCount());
956 CHECK_NE(0, b_with_children->GetChildrenCount());
957
958 const v8::HeapGraphNode* a_from_b = GetChild(
959 b_with_children,
960 v8::HeapGraphNode::kObject,
961 "A");
962 CHECK_NE(NULL, a_from_b);
963 CHECK_EQ(0, a_from_b->GetSelfSize());
964 CHECK_EQ(0, a_from_b->GetInstancesCount());
965 CHECK_EQ(0, a_from_b->GetChildrenCount()); // Retains nothing.
966 CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A.
967}
968
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800969
970TEST(HeapEntryDominator) {
971 // The graph looks like this:
972 //
973 // -> node1
974 // a |^
975 // -> node5 ba
976 // a v|
977 // node6 -> node2
978 // b a |^
979 // -> node4 ba
980 // b v|
981 // -> node3
982 //
983 // The dominator for all nodes is node6.
984
985 v8::HandleScope scope;
986 LocalContext env;
987
988 CompileRun(
989 "function X(a, b) { this.a = a; this.b = b; }\n"
990 "node6 = new X(new X(new X()), new X(new X(),new X()));\n"
991 "(function(){\n"
992 "node6.a.a.b = node6.b.a; // node1 -> node2\n"
993 "node6.b.a.a = node6.a.a; // node2 -> node1\n"
994 "node6.b.a.b = node6.b.b; // node2 -> node3\n"
995 "node6.b.b.a = node6.b.a; // node3 -> node2\n"
996 "})();");
997
998 const v8::HeapSnapshot* snapshot =
999 v8::HeapProfiler::TakeSnapshot(v8::String::New("dominators"));
1000
1001 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1002 CHECK_NE(NULL, global);
1003 const v8::HeapGraphNode* node6 =
1004 GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6");
1005 CHECK_NE(NULL, node6);
1006 const v8::HeapGraphNode* node5 =
1007 GetProperty(node6, v8::HeapGraphEdge::kProperty, "a");
1008 CHECK_NE(NULL, node5);
1009 const v8::HeapGraphNode* node4 =
1010 GetProperty(node6, v8::HeapGraphEdge::kProperty, "b");
1011 CHECK_NE(NULL, node4);
1012 const v8::HeapGraphNode* node3 =
1013 GetProperty(node4, v8::HeapGraphEdge::kProperty, "b");
1014 CHECK_NE(NULL, node3);
1015 const v8::HeapGraphNode* node2 =
1016 GetProperty(node4, v8::HeapGraphEdge::kProperty, "a");
1017 CHECK_NE(NULL, node2);
1018 const v8::HeapGraphNode* node1 =
1019 GetProperty(node5, v8::HeapGraphEdge::kProperty, "a");
1020 CHECK_NE(NULL, node1);
1021
1022 CHECK_EQ(node6, node1->GetDominatorNode());
1023 CHECK_EQ(node6, node2->GetDominatorNode());
1024 CHECK_EQ(node6, node3->GetDominatorNode());
1025 CHECK_EQ(node6, node4->GetDominatorNode());
1026 CHECK_EQ(node6, node5->GetDominatorNode());
1027}
1028
1029
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001030namespace {
1031
1032class TestJSONStream : public v8::OutputStream {
1033 public:
1034 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
1035 explicit TestJSONStream(int abort_countdown)
1036 : eos_signaled_(0), abort_countdown_(abort_countdown) {}
1037 virtual ~TestJSONStream() {}
1038 virtual void EndOfStream() { ++eos_signaled_; }
1039 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
1040 if (abort_countdown_ > 0) --abort_countdown_;
1041 if (abort_countdown_ == 0) return kAbort;
1042 CHECK_GT(chars_written, 0);
1043 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
1044 memcpy(chunk.start(), buffer, chars_written);
1045 return kContinue;
1046 }
1047 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
1048 int eos_signaled() { return eos_signaled_; }
1049 int size() { return buffer_.size(); }
1050 private:
1051 i::Collector<char> buffer_;
1052 int eos_signaled_;
1053 int abort_countdown_;
1054};
1055
1056class AsciiResource: public v8::String::ExternalAsciiStringResource {
1057 public:
1058 explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
1059 length_ = string.length();
1060 }
1061 virtual const char* data() const { return data_; }
1062 virtual size_t length() const { return length_; }
1063 private:
1064 const char* data_;
1065 size_t length_;
1066};
1067
1068} // namespace
1069
1070TEST(HeapSnapshotJSONSerialization) {
1071 v8::HandleScope scope;
1072 LocalContext env;
1073
1074#define STRING_LITERAL_FOR_TEST \
1075 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
Ben Murdochf87a2032010-10-22 12:50:53 +01001076 CompileRun(
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001077 "function A(s) { this.s = s; }\n"
1078 "function B(x) { this.x = x; }\n"
1079 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
1080 "var b = new B(a);");
1081 const v8::HeapSnapshot* snapshot =
1082 v8::HeapProfiler::TakeSnapshot(v8::String::New("json"));
1083 TestJSONStream stream;
1084 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1085 CHECK_GT(stream.size(), 0);
1086 CHECK_EQ(1, stream.eos_signaled());
1087 i::ScopedVector<char> json(stream.size());
1088 stream.WriteTo(json);
1089
1090 // Verify that snapshot string is valid JSON.
1091 AsciiResource json_res(json);
1092 v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
1093 env->Global()->Set(v8::String::New("json_snapshot"), json_string);
1094 v8::Local<v8::Value> snapshot_parse_result = CompileRun(
1095 "var parsed = JSON.parse(json_snapshot); true;");
1096 CHECK(!snapshot_parse_result.IsEmpty());
1097
1098 // Verify that snapshot object has required fields.
1099 v8::Local<v8::Object> parsed_snapshot =
1100 env->Global()->Get(v8::String::New("parsed"))->ToObject();
1101 CHECK(parsed_snapshot->Has(v8::String::New("snapshot")));
1102 CHECK(parsed_snapshot->Has(v8::String::New("nodes")));
1103 CHECK(parsed_snapshot->Has(v8::String::New("strings")));
1104
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001105 // Get node and edge "member" offsets.
1106 v8::Local<v8::Value> meta_analysis_result = CompileRun(
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001107 "var parsed_meta = parsed.nodes[0];\n"
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001108 "var children_count_offset ="
1109 " parsed_meta.fields.indexOf('children_count');\n"
1110 "var children_offset ="
1111 " parsed_meta.fields.indexOf('children');\n"
1112 "var children_meta ="
1113 " parsed_meta.types[children_offset];\n"
1114 "var child_fields_count = children_meta.fields.length;\n"
1115 "var child_type_offset ="
1116 " children_meta.fields.indexOf('type');\n"
1117 "var child_name_offset ="
1118 " children_meta.fields.indexOf('name_or_index');\n"
1119 "var child_to_node_offset ="
1120 " children_meta.fields.indexOf('to_node');\n"
1121 "var property_type ="
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001122 " children_meta.types[child_type_offset].indexOf('property');\n"
1123 "var shortcut_type ="
1124 " children_meta.types[child_type_offset].indexOf('shortcut');");
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001125 CHECK(!meta_analysis_result.IsEmpty());
1126
1127 // A helper function for processing encoded nodes.
1128 CompileRun(
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001129 "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001130 " var nodes = parsed.nodes;\n"
1131 " var strings = parsed.strings;\n"
1132 " for (var i = 0,\n"
1133 " count = nodes[pos + children_count_offset] * child_fields_count;\n"
1134 " i < count; i += child_fields_count) {\n"
1135 " var child_pos = pos + children_offset + i;\n"
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001136 " if (nodes[child_pos + child_type_offset] === prop_type\n"
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001137 " && strings[nodes[child_pos + child_name_offset]] === prop_name)\n"
1138 " return nodes[child_pos + child_to_node_offset];\n"
1139 " }\n"
1140 " return null;\n"
1141 "}\n");
1142 // Get the string index using the path: <root> -> <global>.b.x.s
1143 v8::Local<v8::Value> string_obj_pos_val = CompileRun(
1144 "GetChildPosByProperty(\n"
1145 " GetChildPosByProperty(\n"
1146 " GetChildPosByProperty("
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001147 " parsed.nodes[1 + children_offset + child_to_node_offset],"
1148 " \"b\",shortcut_type),\n"
1149 " \"x\", property_type),"
1150 " \"s\", property_type)");
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001151 CHECK(!string_obj_pos_val.IsEmpty());
1152 int string_obj_pos =
1153 static_cast<int>(string_obj_pos_val->ToNumber()->Value());
1154 v8::Local<v8::Object> nodes_array =
1155 parsed_snapshot->Get(v8::String::New("nodes"))->ToObject();
1156 int string_index = static_cast<int>(
1157 nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
1158 CHECK_GT(string_index, 0);
1159 v8::Local<v8::Object> strings_array =
1160 parsed_snapshot->Get(v8::String::New("strings"))->ToObject();
1161 v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
1162 v8::Local<v8::String> ref_string =
1163 CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
1164#undef STRING_LITERAL_FOR_TEST
1165 CHECK_EQ(*v8::String::Utf8Value(ref_string),
1166 *v8::String::Utf8Value(string));
1167}
1168
1169
1170TEST(HeapSnapshotJSONSerializationAborting) {
1171 v8::HandleScope scope;
1172 LocalContext env;
1173 const v8::HeapSnapshot* snapshot =
1174 v8::HeapProfiler::TakeSnapshot(v8::String::New("abort"));
1175 TestJSONStream stream(5);
1176 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1177 CHECK_GT(stream.size(), 0);
1178 CHECK_EQ(0, stream.eos_signaled());
1179}
1180
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08001181
1182// Must not crash in debug mode.
1183TEST(AggregatedHeapSnapshotJSONSerialization) {
1184 v8::HandleScope scope;
1185 LocalContext env;
1186
1187 const v8::HeapSnapshot* snapshot =
1188 v8::HeapProfiler::TakeSnapshot(
1189 v8::String::New("agg"), v8::HeapSnapshot::kAggregated);
1190 TestJSONStream stream;
1191 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1192 CHECK_GT(stream.size(), 0);
1193 CHECK_EQ(1, stream.eos_signaled());
1194}
1195
Steve Blocka7e24c12009-10-30 11:49:00 +00001196#endif // ENABLE_LOGGING_AND_PROFILING