blob: b86a3361605b5ee79abadb8dff0796a36faef8c1 [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()
391 : has_A1(false), has_B1(false), has_C1(false),
392 has_A2(false), has_B2(false), has_C2(false) {
393 }
394
Iain Merrick75681382010-08-19 15:07:18 +0100395 void Apply(i::HeapEntry** entry_ptr) {
396 if (IsReachableNodeWithName(*entry_ptr, "A1")) has_A1 = true;
397 if (IsReachableNodeWithName(*entry_ptr, "B1")) has_B1 = true;
398 if (IsReachableNodeWithName(*entry_ptr, "C1")) has_C1 = true;
399 if (IsReachableNodeWithName(*entry_ptr, "A2")) has_A2 = true;
400 if (IsReachableNodeWithName(*entry_ptr, "B2")) has_B2 = true;
401 if (IsReachableNodeWithName(*entry_ptr, "C2")) has_C2 = true;
402 }
403
404 static bool IsReachableNodeWithName(i::HeapEntry* entry, const char* name) {
405 return strcmp(name, entry->name()) == 0 && entry->painted_reachable();
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100406 }
407
408 bool has_A1;
409 bool has_B1;
410 bool has_C1;
411 bool has_A2;
412 bool has_B2;
413 bool has_C2;
414};
415
416} // namespace
417
418
419static const v8::HeapGraphNode* GetGlobalObject(
420 const v8::HeapSnapshot* snapshot) {
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100421 CHECK_EQ(1, snapshot->GetRoot()->GetChildrenCount());
422 return snapshot->GetRoot()->GetChild(0)->GetToNode();
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100423}
424
425
426static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
427 v8::HeapGraphEdge::Type type,
428 const char* name) {
429 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
430 const v8::HeapGraphEdge* prop = node->GetChild(i);
431 v8::String::AsciiValue prop_name(prop->GetName());
432 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
433 return prop->GetToNode();
434 }
435 return NULL;
436}
437
438
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100439static bool IsNodeRetainedAs(const v8::HeapGraphNode* node,
440 v8::HeapGraphEdge::Type type,
441 const char* name) {
442 for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) {
443 const v8::HeapGraphEdge* prop = node->GetRetainer(i);
444 v8::String::AsciiValue prop_name(prop->GetName());
445 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
446 return true;
447 }
448 return false;
449}
450
451
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100452static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
453 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
454 const v8::HeapGraphEdge* prop = node->GetChild(i);
455 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100456 if (node->GetType() == v8::HeapGraphNode::kString) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100457 v8::String::AsciiValue node_name(node->GetName());
458 if (strcmp(contents, *node_name) == 0) return true;
459 }
460 }
461 return false;
462}
463
464
465TEST(HeapSnapshot) {
466 v8::HandleScope scope;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100467 v8::Handle<v8::String> token1 = v8::String::New("token1");
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100468 LocalContext env1;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100469 env1->SetSecurityToken(token1);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100470
Ben Murdochf87a2032010-10-22 12:50:53 +0100471 CompileRun(
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100472 "function A1() {}\n"
473 "function B1(x) { this.x = x; }\n"
474 "function C1(x) { this.x1 = x; this.x2 = x; }\n"
475 "var a1 = new A1();\n"
476 "var b1_1 = new B1(a1), b1_2 = new B1(a1);\n"
477 "var c1 = new C1(a1);");
478
479 v8::Handle<v8::String> token2 = v8::String::New("token2");
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100480 LocalContext env2;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100481 env2->SetSecurityToken(token2);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100482
Ben Murdochf87a2032010-10-22 12:50:53 +0100483 CompileRun(
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100484 "function A2() {}\n"
485 "function B2(x) { return function() { return typeof x; }; }\n"
486 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
487 "var a2 = new A2();\n"
488 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
489 "var c2 = new C2(a2);");
490 const v8::HeapSnapshot* snapshot_env2 =
491 v8::HeapProfiler::TakeSnapshot(v8::String::New("env2"));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100492 i::HeapSnapshot* i_snapshot_env2 =
493 const_cast<i::HeapSnapshot*>(
494 reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
Iain Merrick75681382010-08-19 15:07:18 +0100495 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
496 // Paint all nodes reachable from global object.
497 i_snapshot_env2->ClearPaint();
498 const_cast<i::HeapEntry*>(
499 reinterpret_cast<const i::HeapEntry*>(global_env2))->PaintAllReachable();
500
501 // Verify, that JS global object of env2 doesn't have '..1'
502 // properties, but has '..2' properties.
503 CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a1"));
504 CHECK_EQ(
505 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b1_1"));
506 CHECK_EQ(
507 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b1_2"));
508 CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c1"));
509 const v8::HeapGraphNode* a2_node =
510 GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
511 CHECK_NE(NULL, a2_node);
512 CHECK_NE(
513 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
514 CHECK_NE(
515 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
516 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
517
518 // Verify that anything related to '[ABC]1' is not reachable.
519 NamedEntriesDetector det;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100520 i_snapshot_env2->IterateEntries(&det);
521 CHECK(!det.has_A1);
522 CHECK(!det.has_B1);
523 CHECK(!det.has_C1);
524 CHECK(det.has_A2);
525 CHECK(det.has_B2);
526 CHECK(det.has_C2);
527
528 // Verify 'a2' object retainers. They are:
529 // - (global object).a2
530 // - c2.x1, c2.x2, c2[1]
531 // - b2_1 and b2_2 closures: via 'x' variable
532 CHECK_EQ(6, a2_node->GetRetainingPathsCount());
533 bool has_global_obj_a2_ref = false;
534 bool has_c2_x1_ref = false, has_c2_x2_ref = false, has_c2_1_ref = false;
535 bool has_b2_1_x_ref = false, has_b2_2_x_ref = false;
536 for (int i = 0; i < a2_node->GetRetainingPathsCount(); ++i) {
537 const v8::HeapGraphPath* path = a2_node->GetRetainingPath(i);
538 const int edges_count = path->GetEdgesCount();
539 CHECK_GT(edges_count, 0);
540 const v8::HeapGraphEdge* last_edge = path->GetEdge(edges_count - 1);
541 v8::String::AsciiValue last_edge_name(last_edge->GetName());
542 if (strcmp("a2", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100543 && last_edge->GetType() == v8::HeapGraphEdge::kProperty) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100544 has_global_obj_a2_ref = true;
545 continue;
546 }
547 CHECK_GT(edges_count, 1);
548 const v8::HeapGraphEdge* prev_edge = path->GetEdge(edges_count - 2);
549 v8::String::AsciiValue prev_edge_name(prev_edge->GetName());
550 if (strcmp("x1", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100551 && last_edge->GetType() == v8::HeapGraphEdge::kProperty
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100552 && strcmp("c2", *prev_edge_name) == 0) has_c2_x1_ref = true;
553 if (strcmp("x2", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100554 && last_edge->GetType() == v8::HeapGraphEdge::kProperty
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100555 && strcmp("c2", *prev_edge_name) == 0) has_c2_x2_ref = true;
556 if (strcmp("1", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100557 && last_edge->GetType() == v8::HeapGraphEdge::kElement
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100558 && strcmp("c2", *prev_edge_name) == 0) has_c2_1_ref = true;
559 if (strcmp("x", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100560 && last_edge->GetType() == v8::HeapGraphEdge::kContextVariable
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100561 && strcmp("b2_1", *prev_edge_name) == 0) has_b2_1_x_ref = true;
562 if (strcmp("x", *last_edge_name) == 0
Iain Merrick75681382010-08-19 15:07:18 +0100563 && last_edge->GetType() == v8::HeapGraphEdge::kContextVariable
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100564 && strcmp("b2_2", *prev_edge_name) == 0) has_b2_2_x_ref = true;
565 }
566 CHECK(has_global_obj_a2_ref);
567 CHECK(has_c2_x1_ref);
568 CHECK(has_c2_x2_ref);
569 CHECK(has_c2_1_ref);
570 CHECK(has_b2_1_x_ref);
571 CHECK(has_b2_2_x_ref);
572}
573
574
Iain Merrick75681382010-08-19 15:07:18 +0100575TEST(HeapSnapshotObjectSizes) {
576 v8::HandleScope scope;
577 LocalContext env;
578
579 // -a-> X1 --a
580 // x -b-> X2 <-|
Ben Murdochf87a2032010-10-22 12:50:53 +0100581 CompileRun(
Iain Merrick75681382010-08-19 15:07:18 +0100582 "function X(a, b) { this.a = a; this.b = b; }\n"
583 "x = new X(new X(), new X());\n"
584 "x.a.a = x.b;");
585 const v8::HeapSnapshot* snapshot =
586 v8::HeapProfiler::TakeSnapshot(v8::String::New("sizes"));
587 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
588 const v8::HeapGraphNode* x =
589 GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
590 CHECK_NE(NULL, x);
591 const v8::HeapGraphNode* x_prototype =
Ben Murdochf87a2032010-10-22 12:50:53 +0100592 GetProperty(x, v8::HeapGraphEdge::kProperty, "__proto__");
Iain Merrick75681382010-08-19 15:07:18 +0100593 CHECK_NE(NULL, x_prototype);
594 const v8::HeapGraphNode* x1 =
595 GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
596 CHECK_NE(NULL, x1);
597 const v8::HeapGraphNode* x2 =
598 GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
599 CHECK_NE(NULL, x2);
600 CHECK_EQ(
601 x->GetSelfSize() * 3,
602 x->GetReachableSize() - x_prototype->GetReachableSize());
603 CHECK_EQ(
Ben Murdochf87a2032010-10-22 12:50:53 +0100604 x->GetSelfSize() * 3, x->GetRetainedSize());
Iain Merrick75681382010-08-19 15:07:18 +0100605 CHECK_EQ(
606 x1->GetSelfSize() * 2,
607 x1->GetReachableSize() - x_prototype->GetReachableSize());
608 CHECK_EQ(
609 x1->GetSelfSize(), x1->GetRetainedSize());
610 CHECK_EQ(
611 x2->GetSelfSize(),
612 x2->GetReachableSize() - x_prototype->GetReachableSize());
613 CHECK_EQ(
614 x2->GetSelfSize(), x2->GetRetainedSize());
615}
616
617
618TEST(HeapSnapshotEntryChildren) {
619 v8::HandleScope scope;
620 LocalContext env;
621
Ben Murdochf87a2032010-10-22 12:50:53 +0100622 CompileRun(
Iain Merrick75681382010-08-19 15:07:18 +0100623 "function A() { }\n"
624 "a = new A;");
625 const v8::HeapSnapshot* snapshot =
626 v8::HeapProfiler::TakeSnapshot(v8::String::New("children"));
627 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
628 for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
629 const v8::HeapGraphEdge* prop = global->GetChild(i);
630 CHECK_EQ(global, prop->GetFromNode());
631 }
632 const v8::HeapGraphNode* a =
633 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
634 CHECK_NE(NULL, a);
635 for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
636 const v8::HeapGraphEdge* prop = a->GetChild(i);
637 CHECK_EQ(a, prop->GetFromNode());
638 }
639}
640
641
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100642TEST(HeapSnapshotCodeObjects) {
643 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100644 LocalContext env;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100645
Ben Murdochf87a2032010-10-22 12:50:53 +0100646 CompileRun(
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100647 "function lazy(x) { return x - 1; }\n"
648 "function compiled(x) { return x + 1; }\n"
Steve Block791712a2010-08-27 10:21:07 +0100649 "var anonymous = (function() { return function() { return 0; } })();\n"
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100650 "compiled(1)");
651 const v8::HeapSnapshot* snapshot =
652 v8::HeapProfiler::TakeSnapshot(v8::String::New("code"));
653
654 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
655 const v8::HeapGraphNode* compiled =
Iain Merrick75681382010-08-19 15:07:18 +0100656 GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100657 CHECK_NE(NULL, compiled);
Iain Merrick75681382010-08-19 15:07:18 +0100658 CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100659 const v8::HeapGraphNode* lazy =
Iain Merrick75681382010-08-19 15:07:18 +0100660 GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100661 CHECK_NE(NULL, lazy);
Iain Merrick75681382010-08-19 15:07:18 +0100662 CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
Steve Block791712a2010-08-27 10:21:07 +0100663 const v8::HeapGraphNode* anonymous =
664 GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
665 CHECK_NE(NULL, anonymous);
666 CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
667 v8::String::AsciiValue anonymous_name(anonymous->GetName());
Ben Murdochf87a2032010-10-22 12:50:53 +0100668 CHECK_EQ("", *anonymous_name);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100669
670 // Find references to code.
671 const v8::HeapGraphNode* compiled_code =
Iain Merrick75681382010-08-19 15:07:18 +0100672 GetProperty(compiled, v8::HeapGraphEdge::kInternal, "code");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100673 CHECK_NE(NULL, compiled_code);
674 const v8::HeapGraphNode* lazy_code =
Iain Merrick75681382010-08-19 15:07:18 +0100675 GetProperty(lazy, v8::HeapGraphEdge::kInternal, "code");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100676 CHECK_NE(NULL, lazy_code);
677
678 // Verify that non-compiled code doesn't contain references to "x"
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100679 // literal, while compiled code does. The scope info is stored in FixedArray
680 // objects attached to the SharedFunctionInfo.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100681 bool compiled_references_x = false, lazy_references_x = false;
682 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
683 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
684 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100685 if (node->GetType() == v8::HeapGraphNode::kArray) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100686 if (HasString(node, "x")) {
687 compiled_references_x = true;
688 break;
689 }
690 }
691 }
692 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
693 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
694 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100695 if (node->GetType() == v8::HeapGraphNode::kArray) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100696 if (HasString(node, "x")) {
697 lazy_references_x = true;
698 break;
699 }
700 }
701 }
702 CHECK(compiled_references_x);
703 CHECK(!lazy_references_x);
704}
705
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100706
Ben Murdochf87a2032010-10-22 12:50:53 +0100707TEST(HeapSnapshotHeapNumbers) {
708 v8::HandleScope scope;
709 LocalContext env;
710 CompileRun(
711 "a = 1; // a is Smi\n"
712 "b = 2.5; // b is HeapNumber");
713 const v8::HeapSnapshot* snapshot =
714 v8::HeapProfiler::TakeSnapshot(v8::String::New("numbers"));
715 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
716 CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a"));
717 const v8::HeapGraphNode* b =
718 GetProperty(global, v8::HeapGraphEdge::kProperty, "b");
719 CHECK_NE(NULL, b);
720 CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
721}
722
723
724TEST(HeapSnapshotInternalReferences) {
725 v8::HandleScope scope;
726 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
727 global_template->SetInternalFieldCount(2);
728 LocalContext env(NULL, global_template);
729 v8::Handle<v8::Object> global_proxy = env->Global();
730 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
731 CHECK_EQ(2, global->InternalFieldCount());
732 v8::Local<v8::Object> obj = v8::Object::New();
733 global->SetInternalField(0, v8_num(17));
734 global->SetInternalField(1, obj);
735 const v8::HeapSnapshot* snapshot =
736 v8::HeapProfiler::TakeSnapshot(v8::String::New("internals"));
737 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
738 // The first reference will not present, because it's a Smi.
739 CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
740 // The second reference is to an object.
741 CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
742}
743
744
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100745// Trying to introduce a check helper for uint64_t causes many
746// overloading ambiguities, so it seems easier just to cast
747// them to a signed type.
748#define CHECK_EQ_UINT64_T(a, b) \
749 CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b))
Iain Merrick75681382010-08-19 15:07:18 +0100750#define CHECK_NE_UINT64_T(a, b) \
751 CHECK((a) != (b)) // NOLINT
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100752
753TEST(HeapEntryIdsAndGC) {
754 v8::HandleScope scope;
755 LocalContext env;
756
Ben Murdochf87a2032010-10-22 12:50:53 +0100757 CompileRun(
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100758 "function A() {}\n"
759 "function B(x) { this.x = x; }\n"
760 "var a = new A();\n"
761 "var b = new B(a);");
762 const v8::HeapSnapshot* snapshot1 =
763 v8::HeapProfiler::TakeSnapshot(v8::String::New("s1"));
764
765 i::Heap::CollectAllGarbage(true); // Enforce compaction.
766
767 const v8::HeapSnapshot* snapshot2 =
768 v8::HeapProfiler::TakeSnapshot(v8::String::New("s2"));
769
770 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
771 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
772 CHECK_NE_UINT64_T(0, global1->GetId());
773 CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
774 const v8::HeapGraphNode* A1 =
Iain Merrick75681382010-08-19 15:07:18 +0100775 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
776 CHECK_NE(NULL, A1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100777 const v8::HeapGraphNode* A2 =
Iain Merrick75681382010-08-19 15:07:18 +0100778 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
779 CHECK_NE(NULL, A2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100780 CHECK_NE_UINT64_T(0, A1->GetId());
781 CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId());
782 const v8::HeapGraphNode* B1 =
Iain Merrick75681382010-08-19 15:07:18 +0100783 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
784 CHECK_NE(NULL, B1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100785 const v8::HeapGraphNode* B2 =
Iain Merrick75681382010-08-19 15:07:18 +0100786 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
787 CHECK_NE(NULL, B2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100788 CHECK_NE_UINT64_T(0, B1->GetId());
789 CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId());
790 const v8::HeapGraphNode* a1 =
Iain Merrick75681382010-08-19 15:07:18 +0100791 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
792 CHECK_NE(NULL, a1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100793 const v8::HeapGraphNode* a2 =
Iain Merrick75681382010-08-19 15:07:18 +0100794 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
795 CHECK_NE(NULL, a2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100796 CHECK_NE_UINT64_T(0, a1->GetId());
797 CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
798 const v8::HeapGraphNode* b1 =
Iain Merrick75681382010-08-19 15:07:18 +0100799 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
800 CHECK_NE(NULL, b1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100801 const v8::HeapGraphNode* b2 =
Iain Merrick75681382010-08-19 15:07:18 +0100802 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
803 CHECK_NE(NULL, b2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100804 CHECK_NE_UINT64_T(0, b1->GetId());
805 CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId());
806}
807
808
809TEST(HeapSnapshotsDiff) {
810 v8::HandleScope scope;
811 LocalContext env;
812
Ben Murdochf87a2032010-10-22 12:50:53 +0100813 CompileRun(
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100814 "function A() {}\n"
815 "function B(x) { this.x = x; }\n"
Ben Murdochf87a2032010-10-22 12:50:53 +0100816 "function A2(a) { for (var i = 0; i < a; ++i) this[i] = i; }\n"
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100817 "var a = new A();\n"
818 "var b = new B(a);");
819 const v8::HeapSnapshot* snapshot1 =
820 v8::HeapProfiler::TakeSnapshot(v8::String::New("s1"));
821
Ben Murdochf87a2032010-10-22 12:50:53 +0100822 CompileRun(
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100823 "delete a;\n"
824 "b.x = null;\n"
Ben Murdochf87a2032010-10-22 12:50:53 +0100825 "var a = new A2(20);\n"
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100826 "var b2 = new B(a);");
827 const v8::HeapSnapshot* snapshot2 =
828 v8::HeapProfiler::TakeSnapshot(v8::String::New("s2"));
829
830 const v8::HeapSnapshotsDiff* diff = snapshot1->CompareWith(snapshot2);
831
832 // Verify additions: ensure that addition of A and B was detected.
833 const v8::HeapGraphNode* additions_root = diff->GetAdditionsRoot();
834 bool found_A = false, found_B = false;
835 uint64_t s1_A_id = 0;
836 for (int i = 0, count = additions_root->GetChildrenCount(); i < count; ++i) {
837 const v8::HeapGraphEdge* prop = additions_root->GetChild(i);
838 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100839 if (node->GetType() == v8::HeapGraphNode::kObject) {
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100840 v8::String::AsciiValue node_name(node->GetName());
Ben Murdochf87a2032010-10-22 12:50:53 +0100841 if (strcmp(*node_name, "A2") == 0) {
Iain Merrick75681382010-08-19 15:07:18 +0100842 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "a"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100843 CHECK(!found_A);
844 found_A = true;
845 s1_A_id = node->GetId();
846 } else if (strcmp(*node_name, "B") == 0) {
Iain Merrick75681382010-08-19 15:07:18 +0100847 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "b2"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100848 CHECK(!found_B);
849 found_B = true;
850 }
851 }
852 }
853 CHECK(found_A);
854 CHECK(found_B);
855
856 // Verify deletions: ensure that deletion of A was detected.
857 const v8::HeapGraphNode* deletions_root = diff->GetDeletionsRoot();
858 bool found_A_del = false;
859 uint64_t s2_A_id = 0;
860 for (int i = 0, count = deletions_root->GetChildrenCount(); i < count; ++i) {
861 const v8::HeapGraphEdge* prop = deletions_root->GetChild(i);
862 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100863 if (node->GetType() == v8::HeapGraphNode::kObject) {
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100864 v8::String::AsciiValue node_name(node->GetName());
865 if (strcmp(*node_name, "A") == 0) {
Iain Merrick75681382010-08-19 15:07:18 +0100866 CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "a"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100867 CHECK(!found_A_del);
868 found_A_del = true;
869 s2_A_id = node->GetId();
870 }
871 }
872 }
873 CHECK(found_A_del);
874 CHECK_NE_UINT64_T(0, s1_A_id);
875 CHECK(s1_A_id != s2_A_id);
876}
877
Iain Merrick75681382010-08-19 15:07:18 +0100878
Ben Murdochf87a2032010-10-22 12:50:53 +0100879TEST(HeapSnapshotRootPreservedAfterSorting) {
880 v8::HandleScope scope;
881 LocalContext env;
882 const v8::HeapSnapshot* snapshot =
883 v8::HeapProfiler::TakeSnapshot(v8::String::New("s"));
884 const v8::HeapGraphNode* root1 = snapshot->GetRoot();
885 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
886 snapshot))->GetSortedEntriesList();
887 const v8::HeapGraphNode* root2 = snapshot->GetRoot();
888 CHECK_EQ(root1, root2);
889}
890
891
Iain Merrick75681382010-08-19 15:07:18 +0100892namespace v8 {
893namespace internal {
894
895class HeapSnapshotTester {
896 public:
897 static int CalculateNetworkSize(JSObject* obj) {
898 return HeapSnapshot::CalculateNetworkSize(obj);
899 }
900};
901
902} } // namespace v8::internal
903
904// http://code.google.com/p/v8/issues/detail?id=822
905// Trying to call CalculateNetworkSize on an object with elements set
906// to non-FixedArray may cause an assertion error in debug builds.
907TEST(Issue822) {
908 v8::HandleScope scope;
909 LocalContext context;
910 const int kElementCount = 260;
911 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
912 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
913 pixel_data);
914 v8::Handle<v8::Object> obj = v8::Object::New();
915 // Set the elements to be the pixels.
916 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
917 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
918 // This call must not cause an assertion error in debug builds.
919 i::HeapSnapshotTester::CalculateNetworkSize(*jsobj);
920}
921
Steve Block791712a2010-08-27 10:21:07 +0100922
923static const v8::HeapGraphNode* GetChild(
924 const v8::HeapGraphNode* node,
925 v8::HeapGraphNode::Type type,
926 const char* name,
927 const v8::HeapGraphNode* after = NULL) {
928 bool ignore_child = after == NULL ? false : true;
929 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
930 const v8::HeapGraphEdge* prop = node->GetChild(i);
931 const v8::HeapGraphNode* child = prop->GetToNode();
932 v8::String::AsciiValue child_name(child->GetName());
933 if (!ignore_child
934 && child->GetType() == type
935 && strcmp(name, *child_name) == 0)
936 return child;
937 if (after != NULL && child == after) ignore_child = false;
938 }
939 return NULL;
940}
941
942static bool IsNodeRetainedAs(const v8::HeapGraphNode* node,
943 int element) {
944 for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) {
945 const v8::HeapGraphEdge* prop = node->GetRetainer(i);
946 if (prop->GetType() == v8::HeapGraphEdge::kElement
947 && element == prop->GetName()->Int32Value())
948 return true;
949 }
950 return false;
951}
952
953TEST(AggregatedHeapSnapshot) {
954 v8::HandleScope scope;
955 LocalContext env;
956
Ben Murdochf87a2032010-10-22 12:50:53 +0100957 CompileRun(
Steve Block791712a2010-08-27 10:21:07 +0100958 "function A() {}\n"
959 "function B(x) { this.x = x; }\n"
960 "var a = new A();\n"
961 "var b = new B(a);");
962 const v8::HeapSnapshot* snapshot =
963 v8::HeapProfiler::TakeSnapshot(
964 v8::String::New("agg"), v8::HeapSnapshot::kAggregated);
965 const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(),
966 v8::HeapGraphNode::kInternal,
967 "STRING_TYPE");
968 CHECK_NE(NULL, strings);
969 CHECK_NE(0, strings->GetSelfSize());
970 CHECK_NE(0, strings->GetInstancesCount());
971 const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(),
972 v8::HeapGraphNode::kInternal,
973 "MAP_TYPE");
974 CHECK_NE(NULL, maps);
975 CHECK_NE(0, maps->GetSelfSize());
976 CHECK_NE(0, maps->GetInstancesCount());
977
978 const v8::HeapGraphNode* a = GetChild(snapshot->GetRoot(),
979 v8::HeapGraphNode::kObject,
980 "A");
981 CHECK_NE(NULL, a);
982 CHECK_NE(0, a->GetSelfSize());
983 CHECK_EQ(1, a->GetInstancesCount());
984
985 const v8::HeapGraphNode* b = GetChild(snapshot->GetRoot(),
986 v8::HeapGraphNode::kObject,
987 "B");
988 CHECK_NE(NULL, b);
989 CHECK_NE(0, b->GetSelfSize());
990 CHECK_EQ(1, b->GetInstancesCount());
991
992 const v8::HeapGraphNode* glob_prop = GetChild(snapshot->GetRoot(),
993 v8::HeapGraphNode::kObject,
994 "(global property)",
995 b);
996 CHECK_NE(NULL, glob_prop);
997 CHECK_EQ(0, glob_prop->GetSelfSize());
998 CHECK_EQ(0, glob_prop->GetInstancesCount());
999 CHECK_NE(0, glob_prop->GetChildrenCount());
1000
1001 const v8::HeapGraphNode* a_from_glob_prop = GetChild(
1002 glob_prop,
1003 v8::HeapGraphNode::kObject,
1004 "A");
1005 CHECK_NE(NULL, a_from_glob_prop);
1006 CHECK_EQ(0, a_from_glob_prop->GetSelfSize());
1007 CHECK_EQ(0, a_from_glob_prop->GetInstancesCount());
1008 CHECK_EQ(0, a_from_glob_prop->GetChildrenCount()); // Retains nothing.
1009 CHECK(IsNodeRetainedAs(a_from_glob_prop, 1)); // (global propery) has 1 ref.
1010
1011 const v8::HeapGraphNode* b_with_children = GetChild(
1012 snapshot->GetRoot(),
1013 v8::HeapGraphNode::kObject,
1014 "B",
1015 b);
1016 CHECK_NE(NULL, b_with_children);
1017 CHECK_EQ(0, b_with_children->GetSelfSize());
1018 CHECK_EQ(0, b_with_children->GetInstancesCount());
1019 CHECK_NE(0, b_with_children->GetChildrenCount());
1020
1021 const v8::HeapGraphNode* a_from_b = GetChild(
1022 b_with_children,
1023 v8::HeapGraphNode::kObject,
1024 "A");
1025 CHECK_NE(NULL, a_from_b);
1026 CHECK_EQ(0, a_from_b->GetSelfSize());
1027 CHECK_EQ(0, a_from_b->GetInstancesCount());
1028 CHECK_EQ(0, a_from_b->GetChildrenCount()); // Retains nothing.
1029 CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A.
1030}
1031
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001032namespace {
1033
1034class TestJSONStream : public v8::OutputStream {
1035 public:
1036 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
1037 explicit TestJSONStream(int abort_countdown)
1038 : eos_signaled_(0), abort_countdown_(abort_countdown) {}
1039 virtual ~TestJSONStream() {}
1040 virtual void EndOfStream() { ++eos_signaled_; }
1041 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
1042 if (abort_countdown_ > 0) --abort_countdown_;
1043 if (abort_countdown_ == 0) return kAbort;
1044 CHECK_GT(chars_written, 0);
1045 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
1046 memcpy(chunk.start(), buffer, chars_written);
1047 return kContinue;
1048 }
1049 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
1050 int eos_signaled() { return eos_signaled_; }
1051 int size() { return buffer_.size(); }
1052 private:
1053 i::Collector<char> buffer_;
1054 int eos_signaled_;
1055 int abort_countdown_;
1056};
1057
1058class AsciiResource: public v8::String::ExternalAsciiStringResource {
1059 public:
1060 explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
1061 length_ = string.length();
1062 }
1063 virtual const char* data() const { return data_; }
1064 virtual size_t length() const { return length_; }
1065 private:
1066 const char* data_;
1067 size_t length_;
1068};
1069
1070} // namespace
1071
1072TEST(HeapSnapshotJSONSerialization) {
1073 v8::HandleScope scope;
1074 LocalContext env;
1075
1076#define STRING_LITERAL_FOR_TEST \
1077 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
Ben Murdochf87a2032010-10-22 12:50:53 +01001078 CompileRun(
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001079 "function A(s) { this.s = s; }\n"
1080 "function B(x) { this.x = x; }\n"
1081 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
1082 "var b = new B(a);");
1083 const v8::HeapSnapshot* snapshot =
1084 v8::HeapProfiler::TakeSnapshot(v8::String::New("json"));
1085 TestJSONStream stream;
1086 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1087 CHECK_GT(stream.size(), 0);
1088 CHECK_EQ(1, stream.eos_signaled());
1089 i::ScopedVector<char> json(stream.size());
1090 stream.WriteTo(json);
1091
1092 // Verify that snapshot string is valid JSON.
1093 AsciiResource json_res(json);
1094 v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
1095 env->Global()->Set(v8::String::New("json_snapshot"), json_string);
1096 v8::Local<v8::Value> snapshot_parse_result = CompileRun(
1097 "var parsed = JSON.parse(json_snapshot); true;");
1098 CHECK(!snapshot_parse_result.IsEmpty());
1099
1100 // Verify that snapshot object has required fields.
1101 v8::Local<v8::Object> parsed_snapshot =
1102 env->Global()->Get(v8::String::New("parsed"))->ToObject();
1103 CHECK(parsed_snapshot->Has(v8::String::New("snapshot")));
1104 CHECK(parsed_snapshot->Has(v8::String::New("nodes")));
1105 CHECK(parsed_snapshot->Has(v8::String::New("strings")));
1106
1107 // Verify that nodes meta-info is valid JSON.
1108 v8::Local<v8::Value> nodes_meta_parse_result = CompileRun(
1109 "var parsed_meta = JSON.parse(parsed.nodes[0]); true;");
1110 CHECK(!nodes_meta_parse_result.IsEmpty());
1111
1112 // Get node and edge "member" offsets.
1113 v8::Local<v8::Value> meta_analysis_result = CompileRun(
1114 "var children_count_offset ="
1115 " parsed_meta.fields.indexOf('children_count');\n"
1116 "var children_offset ="
1117 " parsed_meta.fields.indexOf('children');\n"
1118 "var children_meta ="
1119 " parsed_meta.types[children_offset];\n"
1120 "var child_fields_count = children_meta.fields.length;\n"
1121 "var child_type_offset ="
1122 " children_meta.fields.indexOf('type');\n"
1123 "var child_name_offset ="
1124 " children_meta.fields.indexOf('name_or_index');\n"
1125 "var child_to_node_offset ="
1126 " children_meta.fields.indexOf('to_node');\n"
1127 "var property_type ="
1128 " children_meta.types[child_type_offset].indexOf('property');");
1129 CHECK(!meta_analysis_result.IsEmpty());
1130
1131 // A helper function for processing encoded nodes.
1132 CompileRun(
1133 "function GetChildPosByProperty(pos, prop_name) {\n"
1134 " var nodes = parsed.nodes;\n"
1135 " var strings = parsed.strings;\n"
1136 " for (var i = 0,\n"
1137 " count = nodes[pos + children_count_offset] * child_fields_count;\n"
1138 " i < count; i += child_fields_count) {\n"
1139 " var child_pos = pos + children_offset + i;\n"
1140 " if (nodes[child_pos + child_type_offset] === property_type\n"
1141 " && strings[nodes[child_pos + child_name_offset]] === prop_name)\n"
1142 " return nodes[child_pos + child_to_node_offset];\n"
1143 " }\n"
1144 " return null;\n"
1145 "}\n");
1146 // Get the string index using the path: <root> -> <global>.b.x.s
1147 v8::Local<v8::Value> string_obj_pos_val = CompileRun(
1148 "GetChildPosByProperty(\n"
1149 " GetChildPosByProperty(\n"
1150 " GetChildPosByProperty("
1151 " parsed.nodes[1 + children_offset + child_to_node_offset],\"b\"),\n"
1152 " \"x\"),"
1153 " \"s\")");
1154 CHECK(!string_obj_pos_val.IsEmpty());
1155 int string_obj_pos =
1156 static_cast<int>(string_obj_pos_val->ToNumber()->Value());
1157 v8::Local<v8::Object> nodes_array =
1158 parsed_snapshot->Get(v8::String::New("nodes"))->ToObject();
1159 int string_index = static_cast<int>(
1160 nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
1161 CHECK_GT(string_index, 0);
1162 v8::Local<v8::Object> strings_array =
1163 parsed_snapshot->Get(v8::String::New("strings"))->ToObject();
1164 v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
1165 v8::Local<v8::String> ref_string =
1166 CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
1167#undef STRING_LITERAL_FOR_TEST
1168 CHECK_EQ(*v8::String::Utf8Value(ref_string),
1169 *v8::String::Utf8Value(string));
1170}
1171
1172
1173TEST(HeapSnapshotJSONSerializationAborting) {
1174 v8::HandleScope scope;
1175 LocalContext env;
1176 const v8::HeapSnapshot* snapshot =
1177 v8::HeapProfiler::TakeSnapshot(v8::String::New("abort"));
1178 TestJSONStream stream(5);
1179 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1180 CHECK_GT(stream.size(), 0);
1181 CHECK_EQ(0, stream.eos_signaled());
1182}
1183
Steve Blocka7e24c12009-10-30 11:49:00 +00001184#endif // ENABLE_LOGGING_AND_PROFILING