blob: b165190b0a98ac953b7993271194c0636eb4917f [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) {
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100414 CHECK_EQ(1, snapshot->GetRoot()->GetChildrenCount());
415 return snapshot->GetRoot()->GetChild(0)->GetToNode();
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100416}
417
418
419static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
420 v8::HeapGraphEdge::Type type,
421 const char* name) {
422 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
423 const v8::HeapGraphEdge* prop = node->GetChild(i);
424 v8::String::AsciiValue prop_name(prop->GetName());
425 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
426 return prop->GetToNode();
427 }
428 return NULL;
429}
430
431
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100432static bool IsNodeRetainedAs(const v8::HeapGraphNode* node,
433 v8::HeapGraphEdge::Type type,
434 const char* name) {
435 for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) {
436 const v8::HeapGraphEdge* prop = node->GetRetainer(i);
437 v8::String::AsciiValue prop_name(prop->GetName());
438 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
439 return true;
440 }
441 return false;
442}
443
444
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100445static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
446 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
447 const v8::HeapGraphEdge* prop = node->GetChild(i);
448 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100449 if (node->GetType() == v8::HeapGraphNode::kString) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100450 v8::String::AsciiValue node_name(node->GetName());
451 if (strcmp(contents, *node_name) == 0) return true;
452 }
453 }
454 return false;
455}
456
457
458TEST(HeapSnapshot) {
459 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100460 LocalContext env2;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100461
Ben Murdochf87a2032010-10-22 12:50:53 +0100462 CompileRun(
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100463 "function A2() {}\n"
464 "function B2(x) { return function() { return typeof x; }; }\n"
465 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
466 "var a2 = new A2();\n"
467 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
468 "var c2 = new C2(a2);");
469 const v8::HeapSnapshot* snapshot_env2 =
470 v8::HeapProfiler::TakeSnapshot(v8::String::New("env2"));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100471 i::HeapSnapshot* i_snapshot_env2 =
472 const_cast<i::HeapSnapshot*>(
473 reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
Iain Merrick75681382010-08-19 15:07:18 +0100474 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
475 // Paint all nodes reachable from global object.
476 i_snapshot_env2->ClearPaint();
477 const_cast<i::HeapEntry*>(
478 reinterpret_cast<const i::HeapEntry*>(global_env2))->PaintAllReachable();
479
Russell Brenner90bac252010-11-18 13:33:46 -0800480 // Verify, that JS global object of env2 has '..2' properties.
Iain Merrick75681382010-08-19 15:07:18 +0100481 const v8::HeapGraphNode* a2_node =
482 GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
483 CHECK_NE(NULL, a2_node);
484 CHECK_NE(
485 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
486 CHECK_NE(
487 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
488 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
489
490 // Verify that anything related to '[ABC]1' is not reachable.
491 NamedEntriesDetector det;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100492 i_snapshot_env2->IterateEntries(&det);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100493 CHECK(det.has_A2);
494 CHECK(det.has_B2);
495 CHECK(det.has_C2);
496
497 // Verify 'a2' object retainers. They are:
498 // - (global object).a2
499 // - c2.x1, c2.x2, c2[1]
500 // - b2_1 and b2_2 closures: via 'x' variable
501 CHECK_EQ(6, a2_node->GetRetainingPathsCount());
502 bool has_global_obj_a2_ref = false;
503 bool has_c2_x1_ref = false, has_c2_x2_ref = false, has_c2_1_ref = false;
504 bool has_b2_1_x_ref = false, has_b2_2_x_ref = false;
505 for (int i = 0; i < a2_node->GetRetainingPathsCount(); ++i) {
506 const v8::HeapGraphPath* path = a2_node->GetRetainingPath(i);
507 const int edges_count = path->GetEdgesCount();
508 CHECK_GT(edges_count, 0);
509 const v8::HeapGraphEdge* last_edge = path->GetEdge(edges_count - 1);
510 v8::String::AsciiValue last_edge_name(last_edge->GetName());
511 if (strcmp("a2", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100512 && last_edge->GetType() == v8::HeapGraphEdge::kProperty) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100513 has_global_obj_a2_ref = true;
514 continue;
515 }
516 CHECK_GT(edges_count, 1);
517 const v8::HeapGraphEdge* prev_edge = path->GetEdge(edges_count - 2);
518 v8::String::AsciiValue prev_edge_name(prev_edge->GetName());
519 if (strcmp("x1", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100520 && last_edge->GetType() == v8::HeapGraphEdge::kProperty
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100521 && strcmp("c2", *prev_edge_name) == 0) has_c2_x1_ref = true;
522 if (strcmp("x2", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100523 && last_edge->GetType() == v8::HeapGraphEdge::kProperty
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100524 && strcmp("c2", *prev_edge_name) == 0) has_c2_x2_ref = true;
525 if (strcmp("1", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100526 && last_edge->GetType() == v8::HeapGraphEdge::kElement
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100527 && strcmp("c2", *prev_edge_name) == 0) has_c2_1_ref = true;
528 if (strcmp("x", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100529 && last_edge->GetType() == v8::HeapGraphEdge::kContextVariable
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100530 && strcmp("b2_1", *prev_edge_name) == 0) has_b2_1_x_ref = true;
531 if (strcmp("x", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100532 && last_edge->GetType() == v8::HeapGraphEdge::kContextVariable
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100533 && strcmp("b2_2", *prev_edge_name) == 0) has_b2_2_x_ref = true;
534 }
535 CHECK(has_global_obj_a2_ref);
536 CHECK(has_c2_x1_ref);
537 CHECK(has_c2_x2_ref);
538 CHECK(has_c2_1_ref);
539 CHECK(has_b2_1_x_ref);
540 CHECK(has_b2_2_x_ref);
541}
542
543
Iain Merrick75681382010-08-19 15:07:18 +0100544TEST(HeapSnapshotObjectSizes) {
545 v8::HandleScope scope;
546 LocalContext env;
547
548 // -a-> X1 --a
549 // x -b-> X2 <-|
Ben Murdochf87a2032010-10-22 12:50:53 +0100550 CompileRun(
Iain Merrick75681382010-08-19 15:07:18 +0100551 "function X(a, b) { this.a = a; this.b = b; }\n"
552 "x = new X(new X(), new X());\n"
553 "x.a.a = x.b;");
554 const v8::HeapSnapshot* snapshot =
555 v8::HeapProfiler::TakeSnapshot(v8::String::New("sizes"));
556 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
557 const v8::HeapGraphNode* x =
558 GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
559 CHECK_NE(NULL, x);
560 const v8::HeapGraphNode* x_prototype =
Ben Murdochf87a2032010-10-22 12:50:53 +0100561 GetProperty(x, v8::HeapGraphEdge::kProperty, "__proto__");
Iain Merrick75681382010-08-19 15:07:18 +0100562 CHECK_NE(NULL, x_prototype);
563 const v8::HeapGraphNode* x1 =
564 GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
565 CHECK_NE(NULL, x1);
566 const v8::HeapGraphNode* x2 =
567 GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
568 CHECK_NE(NULL, x2);
569 CHECK_EQ(
570 x->GetSelfSize() * 3,
571 x->GetReachableSize() - x_prototype->GetReachableSize());
572 CHECK_EQ(
Ben Murdochf87a2032010-10-22 12:50:53 +0100573 x->GetSelfSize() * 3, x->GetRetainedSize());
Iain Merrick75681382010-08-19 15:07:18 +0100574 CHECK_EQ(
575 x1->GetSelfSize() * 2,
576 x1->GetReachableSize() - x_prototype->GetReachableSize());
577 CHECK_EQ(
578 x1->GetSelfSize(), x1->GetRetainedSize());
579 CHECK_EQ(
580 x2->GetSelfSize(),
581 x2->GetReachableSize() - x_prototype->GetReachableSize());
582 CHECK_EQ(
583 x2->GetSelfSize(), x2->GetRetainedSize());
584}
585
586
587TEST(HeapSnapshotEntryChildren) {
588 v8::HandleScope scope;
589 LocalContext env;
590
Ben Murdochf87a2032010-10-22 12:50:53 +0100591 CompileRun(
Iain Merrick75681382010-08-19 15:07:18 +0100592 "function A() { }\n"
593 "a = new A;");
594 const v8::HeapSnapshot* snapshot =
595 v8::HeapProfiler::TakeSnapshot(v8::String::New("children"));
596 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
597 for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
598 const v8::HeapGraphEdge* prop = global->GetChild(i);
599 CHECK_EQ(global, prop->GetFromNode());
600 }
601 const v8::HeapGraphNode* a =
602 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
603 CHECK_NE(NULL, a);
604 for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
605 const v8::HeapGraphEdge* prop = a->GetChild(i);
606 CHECK_EQ(a, prop->GetFromNode());
607 }
608}
609
610
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100611TEST(HeapSnapshotCodeObjects) {
612 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100613 LocalContext env;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100614
Ben Murdochf87a2032010-10-22 12:50:53 +0100615 CompileRun(
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100616 "function lazy(x) { return x - 1; }\n"
617 "function compiled(x) { return x + 1; }\n"
Steve Block791712a2010-08-27 10:21:07 +0100618 "var anonymous = (function() { return function() { return 0; } })();\n"
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100619 "compiled(1)");
620 const v8::HeapSnapshot* snapshot =
621 v8::HeapProfiler::TakeSnapshot(v8::String::New("code"));
622
623 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
624 const v8::HeapGraphNode* compiled =
Iain Merrick75681382010-08-19 15:07:18 +0100625 GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100626 CHECK_NE(NULL, compiled);
Iain Merrick75681382010-08-19 15:07:18 +0100627 CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100628 const v8::HeapGraphNode* lazy =
Iain Merrick75681382010-08-19 15:07:18 +0100629 GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100630 CHECK_NE(NULL, lazy);
Iain Merrick75681382010-08-19 15:07:18 +0100631 CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
Steve Block791712a2010-08-27 10:21:07 +0100632 const v8::HeapGraphNode* anonymous =
633 GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
634 CHECK_NE(NULL, anonymous);
635 CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
636 v8::String::AsciiValue anonymous_name(anonymous->GetName());
Ben Murdochf87a2032010-10-22 12:50:53 +0100637 CHECK_EQ("", *anonymous_name);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100638
639 // Find references to code.
640 const v8::HeapGraphNode* compiled_code =
Iain Merrick75681382010-08-19 15:07:18 +0100641 GetProperty(compiled, v8::HeapGraphEdge::kInternal, "code");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100642 CHECK_NE(NULL, compiled_code);
643 const v8::HeapGraphNode* lazy_code =
Iain Merrick75681382010-08-19 15:07:18 +0100644 GetProperty(lazy, v8::HeapGraphEdge::kInternal, "code");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100645 CHECK_NE(NULL, lazy_code);
646
647 // Verify that non-compiled code doesn't contain references to "x"
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100648 // literal, while compiled code does. The scope info is stored in FixedArray
649 // objects attached to the SharedFunctionInfo.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100650 bool compiled_references_x = false, lazy_references_x = false;
651 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
652 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
653 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100654 if (node->GetType() == v8::HeapGraphNode::kArray) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100655 if (HasString(node, "x")) {
656 compiled_references_x = true;
657 break;
658 }
659 }
660 }
661 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
662 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
663 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100664 if (node->GetType() == v8::HeapGraphNode::kArray) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100665 if (HasString(node, "x")) {
666 lazy_references_x = true;
667 break;
668 }
669 }
670 }
671 CHECK(compiled_references_x);
672 CHECK(!lazy_references_x);
673}
674
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100675
Ben Murdochf87a2032010-10-22 12:50:53 +0100676TEST(HeapSnapshotHeapNumbers) {
677 v8::HandleScope scope;
678 LocalContext env;
679 CompileRun(
680 "a = 1; // a is Smi\n"
681 "b = 2.5; // b is HeapNumber");
682 const v8::HeapSnapshot* snapshot =
683 v8::HeapProfiler::TakeSnapshot(v8::String::New("numbers"));
684 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
685 CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a"));
686 const v8::HeapGraphNode* b =
687 GetProperty(global, v8::HeapGraphEdge::kProperty, "b");
688 CHECK_NE(NULL, b);
689 CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
690}
691
692
693TEST(HeapSnapshotInternalReferences) {
694 v8::HandleScope scope;
695 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
696 global_template->SetInternalFieldCount(2);
697 LocalContext env(NULL, global_template);
698 v8::Handle<v8::Object> global_proxy = env->Global();
699 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
700 CHECK_EQ(2, global->InternalFieldCount());
701 v8::Local<v8::Object> obj = v8::Object::New();
702 global->SetInternalField(0, v8_num(17));
703 global->SetInternalField(1, obj);
704 const v8::HeapSnapshot* snapshot =
705 v8::HeapProfiler::TakeSnapshot(v8::String::New("internals"));
706 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
707 // The first reference will not present, because it's a Smi.
708 CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
709 // The second reference is to an object.
710 CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
711}
712
713
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100714// Trying to introduce a check helper for uint64_t causes many
715// overloading ambiguities, so it seems easier just to cast
716// them to a signed type.
717#define CHECK_EQ_UINT64_T(a, b) \
718 CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b))
Iain Merrick75681382010-08-19 15:07:18 +0100719#define CHECK_NE_UINT64_T(a, b) \
720 CHECK((a) != (b)) // NOLINT
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100721
722TEST(HeapEntryIdsAndGC) {
723 v8::HandleScope scope;
724 LocalContext env;
725
Ben Murdochf87a2032010-10-22 12:50:53 +0100726 CompileRun(
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100727 "function A() {}\n"
728 "function B(x) { this.x = x; }\n"
729 "var a = new A();\n"
730 "var b = new B(a);");
731 const v8::HeapSnapshot* snapshot1 =
732 v8::HeapProfiler::TakeSnapshot(v8::String::New("s1"));
733
734 i::Heap::CollectAllGarbage(true); // Enforce compaction.
735
736 const v8::HeapSnapshot* snapshot2 =
737 v8::HeapProfiler::TakeSnapshot(v8::String::New("s2"));
738
739 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
740 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
741 CHECK_NE_UINT64_T(0, global1->GetId());
742 CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
743 const v8::HeapGraphNode* A1 =
Iain Merrick75681382010-08-19 15:07:18 +0100744 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
745 CHECK_NE(NULL, A1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100746 const v8::HeapGraphNode* A2 =
Iain Merrick75681382010-08-19 15:07:18 +0100747 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
748 CHECK_NE(NULL, A2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100749 CHECK_NE_UINT64_T(0, A1->GetId());
750 CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId());
751 const v8::HeapGraphNode* B1 =
Iain Merrick75681382010-08-19 15:07:18 +0100752 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
753 CHECK_NE(NULL, B1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100754 const v8::HeapGraphNode* B2 =
Iain Merrick75681382010-08-19 15:07:18 +0100755 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
756 CHECK_NE(NULL, B2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100757 CHECK_NE_UINT64_T(0, B1->GetId());
758 CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId());
759 const v8::HeapGraphNode* a1 =
Iain Merrick75681382010-08-19 15:07:18 +0100760 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
761 CHECK_NE(NULL, a1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100762 const v8::HeapGraphNode* a2 =
Iain Merrick75681382010-08-19 15:07:18 +0100763 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
764 CHECK_NE(NULL, a2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100765 CHECK_NE_UINT64_T(0, a1->GetId());
766 CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
767 const v8::HeapGraphNode* b1 =
Iain Merrick75681382010-08-19 15:07:18 +0100768 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
769 CHECK_NE(NULL, b1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100770 const v8::HeapGraphNode* b2 =
Iain Merrick75681382010-08-19 15:07:18 +0100771 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
772 CHECK_NE(NULL, b2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100773 CHECK_NE_UINT64_T(0, b1->GetId());
774 CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId());
775}
776
777
778TEST(HeapSnapshotsDiff) {
779 v8::HandleScope scope;
780 LocalContext env;
781
Ben Murdochf87a2032010-10-22 12:50:53 +0100782 CompileRun(
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100783 "function A() {}\n"
784 "function B(x) { this.x = x; }\n"
Ben Murdochf87a2032010-10-22 12:50:53 +0100785 "function A2(a) { for (var i = 0; i < a; ++i) this[i] = i; }\n"
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100786 "var a = new A();\n"
787 "var b = new B(a);");
788 const v8::HeapSnapshot* snapshot1 =
789 v8::HeapProfiler::TakeSnapshot(v8::String::New("s1"));
790
Ben Murdochf87a2032010-10-22 12:50:53 +0100791 CompileRun(
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100792 "delete a;\n"
793 "b.x = null;\n"
Ben Murdochf87a2032010-10-22 12:50:53 +0100794 "var a = new A2(20);\n"
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100795 "var b2 = new B(a);");
796 const v8::HeapSnapshot* snapshot2 =
797 v8::HeapProfiler::TakeSnapshot(v8::String::New("s2"));
798
799 const v8::HeapSnapshotsDiff* diff = snapshot1->CompareWith(snapshot2);
800
801 // Verify additions: ensure that addition of A and B was detected.
802 const v8::HeapGraphNode* additions_root = diff->GetAdditionsRoot();
803 bool found_A = false, found_B = false;
804 uint64_t s1_A_id = 0;
805 for (int i = 0, count = additions_root->GetChildrenCount(); i < count; ++i) {
806 const v8::HeapGraphEdge* prop = additions_root->GetChild(i);
807 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100808 if (node->GetType() == v8::HeapGraphNode::kObject) {
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100809 v8::String::AsciiValue node_name(node->GetName());
Ben Murdochf87a2032010-10-22 12:50:53 +0100810 if (strcmp(*node_name, "A2") == 0) {
Iain Merrick75681382010-08-19 15:07:18 +0100811 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "a"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100812 CHECK(!found_A);
813 found_A = true;
814 s1_A_id = node->GetId();
815 } else if (strcmp(*node_name, "B") == 0) {
Iain Merrick75681382010-08-19 15:07:18 +0100816 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "b2"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100817 CHECK(!found_B);
818 found_B = true;
819 }
820 }
821 }
822 CHECK(found_A);
823 CHECK(found_B);
824
825 // Verify deletions: ensure that deletion of A was detected.
826 const v8::HeapGraphNode* deletions_root = diff->GetDeletionsRoot();
827 bool found_A_del = false;
828 uint64_t s2_A_id = 0;
829 for (int i = 0, count = deletions_root->GetChildrenCount(); i < count; ++i) {
830 const v8::HeapGraphEdge* prop = deletions_root->GetChild(i);
831 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100832 if (node->GetType() == v8::HeapGraphNode::kObject) {
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100833 v8::String::AsciiValue node_name(node->GetName());
834 if (strcmp(*node_name, "A") == 0) {
Iain Merrick75681382010-08-19 15:07:18 +0100835 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "a"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100836 CHECK(!found_A_del);
837 found_A_del = true;
838 s2_A_id = node->GetId();
839 }
840 }
841 }
842 CHECK(found_A_del);
843 CHECK_NE_UINT64_T(0, s1_A_id);
844 CHECK(s1_A_id != s2_A_id);
845}
846
Iain Merrick75681382010-08-19 15:07:18 +0100847
Ben Murdochf87a2032010-10-22 12:50:53 +0100848TEST(HeapSnapshotRootPreservedAfterSorting) {
849 v8::HandleScope scope;
850 LocalContext env;
851 const v8::HeapSnapshot* snapshot =
852 v8::HeapProfiler::TakeSnapshot(v8::String::New("s"));
853 const v8::HeapGraphNode* root1 = snapshot->GetRoot();
854 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
855 snapshot))->GetSortedEntriesList();
856 const v8::HeapGraphNode* root2 = snapshot->GetRoot();
857 CHECK_EQ(root1, root2);
858}
859
860
Iain Merrick75681382010-08-19 15:07:18 +0100861namespace v8 {
862namespace internal {
863
864class HeapSnapshotTester {
865 public:
866 static int CalculateNetworkSize(JSObject* obj) {
867 return HeapSnapshot::CalculateNetworkSize(obj);
868 }
869};
870
871} } // namespace v8::internal
872
873// http://code.google.com/p/v8/issues/detail?id=822
874// Trying to call CalculateNetworkSize on an object with elements set
875// to non-FixedArray may cause an assertion error in debug builds.
876TEST(Issue822) {
877 v8::HandleScope scope;
878 LocalContext context;
879 const int kElementCount = 260;
880 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
881 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
882 pixel_data);
883 v8::Handle<v8::Object> obj = v8::Object::New();
884 // Set the elements to be the pixels.
885 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
886 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
887 // This call must not cause an assertion error in debug builds.
888 i::HeapSnapshotTester::CalculateNetworkSize(*jsobj);
889}
890
Steve Block791712a2010-08-27 10:21:07 +0100891
892static const v8::HeapGraphNode* GetChild(
893 const v8::HeapGraphNode* node,
894 v8::HeapGraphNode::Type type,
895 const char* name,
896 const v8::HeapGraphNode* after = NULL) {
897 bool ignore_child = after == NULL ? false : true;
898 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
899 const v8::HeapGraphEdge* prop = node->GetChild(i);
900 const v8::HeapGraphNode* child = prop->GetToNode();
901 v8::String::AsciiValue child_name(child->GetName());
902 if (!ignore_child
903 && child->GetType() == type
904 && strcmp(name, *child_name) == 0)
905 return child;
906 if (after != NULL && child == after) ignore_child = false;
907 }
908 return NULL;
909}
910
911static bool IsNodeRetainedAs(const v8::HeapGraphNode* node,
912 int element) {
913 for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) {
914 const v8::HeapGraphEdge* prop = node->GetRetainer(i);
915 if (prop->GetType() == v8::HeapGraphEdge::kElement
916 && element == prop->GetName()->Int32Value())
917 return true;
918 }
919 return false;
920}
921
922TEST(AggregatedHeapSnapshot) {
923 v8::HandleScope scope;
924 LocalContext env;
925
Ben Murdochf87a2032010-10-22 12:50:53 +0100926 CompileRun(
Steve Block791712a2010-08-27 10:21:07 +0100927 "function A() {}\n"
928 "function B(x) { this.x = x; }\n"
929 "var a = new A();\n"
930 "var b = new B(a);");
931 const v8::HeapSnapshot* snapshot =
932 v8::HeapProfiler::TakeSnapshot(
933 v8::String::New("agg"), v8::HeapSnapshot::kAggregated);
934 const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(),
935 v8::HeapGraphNode::kInternal,
936 "STRING_TYPE");
937 CHECK_NE(NULL, strings);
938 CHECK_NE(0, strings->GetSelfSize());
939 CHECK_NE(0, strings->GetInstancesCount());
940 const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(),
941 v8::HeapGraphNode::kInternal,
942 "MAP_TYPE");
943 CHECK_NE(NULL, maps);
944 CHECK_NE(0, maps->GetSelfSize());
945 CHECK_NE(0, maps->GetInstancesCount());
946
947 const v8::HeapGraphNode* a = GetChild(snapshot->GetRoot(),
948 v8::HeapGraphNode::kObject,
949 "A");
950 CHECK_NE(NULL, a);
951 CHECK_NE(0, a->GetSelfSize());
952 CHECK_EQ(1, a->GetInstancesCount());
953
954 const v8::HeapGraphNode* b = GetChild(snapshot->GetRoot(),
955 v8::HeapGraphNode::kObject,
956 "B");
957 CHECK_NE(NULL, b);
958 CHECK_NE(0, b->GetSelfSize());
959 CHECK_EQ(1, b->GetInstancesCount());
960
961 const v8::HeapGraphNode* glob_prop = GetChild(snapshot->GetRoot(),
962 v8::HeapGraphNode::kObject,
963 "(global property)",
964 b);
965 CHECK_NE(NULL, glob_prop);
966 CHECK_EQ(0, glob_prop->GetSelfSize());
967 CHECK_EQ(0, glob_prop->GetInstancesCount());
968 CHECK_NE(0, glob_prop->GetChildrenCount());
969
970 const v8::HeapGraphNode* a_from_glob_prop = GetChild(
971 glob_prop,
972 v8::HeapGraphNode::kObject,
973 "A");
974 CHECK_NE(NULL, a_from_glob_prop);
975 CHECK_EQ(0, a_from_glob_prop->GetSelfSize());
976 CHECK_EQ(0, a_from_glob_prop->GetInstancesCount());
977 CHECK_EQ(0, a_from_glob_prop->GetChildrenCount()); // Retains nothing.
978 CHECK(IsNodeRetainedAs(a_from_glob_prop, 1)); // (global propery) has 1 ref.
979
980 const v8::HeapGraphNode* b_with_children = GetChild(
981 snapshot->GetRoot(),
982 v8::HeapGraphNode::kObject,
983 "B",
984 b);
985 CHECK_NE(NULL, b_with_children);
986 CHECK_EQ(0, b_with_children->GetSelfSize());
987 CHECK_EQ(0, b_with_children->GetInstancesCount());
988 CHECK_NE(0, b_with_children->GetChildrenCount());
989
990 const v8::HeapGraphNode* a_from_b = GetChild(
991 b_with_children,
992 v8::HeapGraphNode::kObject,
993 "A");
994 CHECK_NE(NULL, a_from_b);
995 CHECK_EQ(0, a_from_b->GetSelfSize());
996 CHECK_EQ(0, a_from_b->GetInstancesCount());
997 CHECK_EQ(0, a_from_b->GetChildrenCount()); // Retains nothing.
998 CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A.
999}
1000
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001001namespace {
1002
1003class TestJSONStream : public v8::OutputStream {
1004 public:
1005 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
1006 explicit TestJSONStream(int abort_countdown)
1007 : eos_signaled_(0), abort_countdown_(abort_countdown) {}
1008 virtual ~TestJSONStream() {}
1009 virtual void EndOfStream() { ++eos_signaled_; }
1010 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
1011 if (abort_countdown_ > 0) --abort_countdown_;
1012 if (abort_countdown_ == 0) return kAbort;
1013 CHECK_GT(chars_written, 0);
1014 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
1015 memcpy(chunk.start(), buffer, chars_written);
1016 return kContinue;
1017 }
1018 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
1019 int eos_signaled() { return eos_signaled_; }
1020 int size() { return buffer_.size(); }
1021 private:
1022 i::Collector<char> buffer_;
1023 int eos_signaled_;
1024 int abort_countdown_;
1025};
1026
1027class AsciiResource: public v8::String::ExternalAsciiStringResource {
1028 public:
1029 explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
1030 length_ = string.length();
1031 }
1032 virtual const char* data() const { return data_; }
1033 virtual size_t length() const { return length_; }
1034 private:
1035 const char* data_;
1036 size_t length_;
1037};
1038
1039} // namespace
1040
1041TEST(HeapSnapshotJSONSerialization) {
1042 v8::HandleScope scope;
1043 LocalContext env;
1044
1045#define STRING_LITERAL_FOR_TEST \
1046 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
Ben Murdochf87a2032010-10-22 12:50:53 +01001047 CompileRun(
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001048 "function A(s) { this.s = s; }\n"
1049 "function B(x) { this.x = x; }\n"
1050 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
1051 "var b = new B(a);");
1052 const v8::HeapSnapshot* snapshot =
1053 v8::HeapProfiler::TakeSnapshot(v8::String::New("json"));
1054 TestJSONStream stream;
1055 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1056 CHECK_GT(stream.size(), 0);
1057 CHECK_EQ(1, stream.eos_signaled());
1058 i::ScopedVector<char> json(stream.size());
1059 stream.WriteTo(json);
1060
1061 // Verify that snapshot string is valid JSON.
1062 AsciiResource json_res(json);
1063 v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
1064 env->Global()->Set(v8::String::New("json_snapshot"), json_string);
1065 v8::Local<v8::Value> snapshot_parse_result = CompileRun(
1066 "var parsed = JSON.parse(json_snapshot); true;");
1067 CHECK(!snapshot_parse_result.IsEmpty());
1068
1069 // Verify that snapshot object has required fields.
1070 v8::Local<v8::Object> parsed_snapshot =
1071 env->Global()->Get(v8::String::New("parsed"))->ToObject();
1072 CHECK(parsed_snapshot->Has(v8::String::New("snapshot")));
1073 CHECK(parsed_snapshot->Has(v8::String::New("nodes")));
1074 CHECK(parsed_snapshot->Has(v8::String::New("strings")));
1075
1076 // Verify that nodes meta-info is valid JSON.
1077 v8::Local<v8::Value> nodes_meta_parse_result = CompileRun(
1078 "var parsed_meta = JSON.parse(parsed.nodes[0]); true;");
1079 CHECK(!nodes_meta_parse_result.IsEmpty());
1080
1081 // Get node and edge "member" offsets.
1082 v8::Local<v8::Value> meta_analysis_result = CompileRun(
1083 "var children_count_offset ="
1084 " parsed_meta.fields.indexOf('children_count');\n"
1085 "var children_offset ="
1086 " parsed_meta.fields.indexOf('children');\n"
1087 "var children_meta ="
1088 " parsed_meta.types[children_offset];\n"
1089 "var child_fields_count = children_meta.fields.length;\n"
1090 "var child_type_offset ="
1091 " children_meta.fields.indexOf('type');\n"
1092 "var child_name_offset ="
1093 " children_meta.fields.indexOf('name_or_index');\n"
1094 "var child_to_node_offset ="
1095 " children_meta.fields.indexOf('to_node');\n"
1096 "var property_type ="
1097 " children_meta.types[child_type_offset].indexOf('property');");
1098 CHECK(!meta_analysis_result.IsEmpty());
1099
1100 // A helper function for processing encoded nodes.
1101 CompileRun(
1102 "function GetChildPosByProperty(pos, prop_name) {\n"
1103 " var nodes = parsed.nodes;\n"
1104 " var strings = parsed.strings;\n"
1105 " for (var i = 0,\n"
1106 " count = nodes[pos + children_count_offset] * child_fields_count;\n"
1107 " i < count; i += child_fields_count) {\n"
1108 " var child_pos = pos + children_offset + i;\n"
1109 " if (nodes[child_pos + child_type_offset] === property_type\n"
1110 " && strings[nodes[child_pos + child_name_offset]] === prop_name)\n"
1111 " return nodes[child_pos + child_to_node_offset];\n"
1112 " }\n"
1113 " return null;\n"
1114 "}\n");
1115 // Get the string index using the path: <root> -> <global>.b.x.s
1116 v8::Local<v8::Value> string_obj_pos_val = CompileRun(
1117 "GetChildPosByProperty(\n"
1118 " GetChildPosByProperty(\n"
1119 " GetChildPosByProperty("
1120 " parsed.nodes[1 + children_offset + child_to_node_offset],\"b\"),\n"
1121 " \"x\"),"
1122 " \"s\")");
1123 CHECK(!string_obj_pos_val.IsEmpty());
1124 int string_obj_pos =
1125 static_cast<int>(string_obj_pos_val->ToNumber()->Value());
1126 v8::Local<v8::Object> nodes_array =
1127 parsed_snapshot->Get(v8::String::New("nodes"))->ToObject();
1128 int string_index = static_cast<int>(
1129 nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
1130 CHECK_GT(string_index, 0);
1131 v8::Local<v8::Object> strings_array =
1132 parsed_snapshot->Get(v8::String::New("strings"))->ToObject();
1133 v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
1134 v8::Local<v8::String> ref_string =
1135 CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
1136#undef STRING_LITERAL_FOR_TEST
1137 CHECK_EQ(*v8::String::Utf8Value(ref_string),
1138 *v8::String::Utf8Value(string));
1139}
1140
1141
1142TEST(HeapSnapshotJSONSerializationAborting) {
1143 v8::HandleScope scope;
1144 LocalContext env;
1145 const v8::HeapSnapshot* snapshot =
1146 v8::HeapProfiler::TakeSnapshot(v8::String::New("abort"));
1147 TestJSONStream stream(5);
1148 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1149 CHECK_GT(stream.size(), 0);
1150 CHECK_EQ(0, stream.eos_signaled());
1151}
1152
Steve Blocka7e24c12009-10-30 11:49:00 +00001153#endif // ENABLE_LOGGING_AND_PROFILING