OLD | NEW |
1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 the V8 project authors. All rights reserved. |
2 // | 2 // |
3 // Tests for heap profiler | 3 // Tests for heap profiler |
4 | 4 |
5 #ifdef ENABLE_LOGGING_AND_PROFILING | 5 #ifdef ENABLE_LOGGING_AND_PROFILING |
6 | 6 |
7 #include "v8.h" | 7 #include "v8.h" |
8 | 8 |
9 #include "cctest.h" | 9 #include "cctest.h" |
10 #include "heap-profiler.h" | 10 #include "heap-profiler.h" |
11 #include "snapshot.h" | 11 #include "snapshot.h" |
12 #include "string-stream.h" | |
13 #include "utils-inl.h" | 12 #include "utils-inl.h" |
14 #include "zone-inl.h" | |
15 #include "../include/v8-profiler.h" | 13 #include "../include/v8-profiler.h" |
16 | 14 |
17 namespace i = v8::internal; | 15 namespace i = v8::internal; |
18 using i::ClustersCoarser; | |
19 using i::JSObjectsCluster; | |
20 using i::JSObjectsRetainerTree; | |
21 using i::JSObjectsClusterTree; | |
22 using i::RetainerHeapProfile; | |
23 | |
24 | 16 |
25 namespace { | 17 namespace { |
26 | 18 |
27 class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile { | |
28 public: | |
29 ConstructorHeapProfileTestHelper() | |
30 : i::ConstructorHeapProfile(), | |
31 f_name_(FACTORY->NewStringFromAscii(i::CStrVector("F"))), | |
32 f_count_(0) { | |
33 } | |
34 | |
35 void Call(const JSObjectsCluster& cluster, | |
36 const i::NumberAndSizeInfo& number_and_size) { | |
37 if (f_name_->Equals(cluster.constructor())) { | |
38 CHECK_EQ(f_count_, 0); | |
39 f_count_ = number_and_size.number(); | |
40 CHECK_GT(f_count_, 0); | |
41 } | |
42 } | |
43 | |
44 int f_count() { return f_count_; } | |
45 | |
46 private: | |
47 i::Handle<i::String> f_name_; | |
48 int f_count_; | |
49 }; | |
50 | |
51 } // namespace | |
52 | |
53 | |
54 TEST(ConstructorProfile) { | |
55 v8::HandleScope scope; | |
56 LocalContext env; | |
57 | |
58 CompileRun( | |
59 "function F() {} // A constructor\n" | |
60 "var f1 = new F();\n" | |
61 "var f2 = new F();\n"); | |
62 | |
63 ConstructorHeapProfileTestHelper cons_profile; | |
64 i::AssertNoAllocation no_alloc; | |
65 i::HeapIterator iterator; | |
66 for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) | |
67 cons_profile.CollectStats(obj); | |
68 CHECK_EQ(0, cons_profile.f_count()); | |
69 cons_profile.PrintStats(); | |
70 CHECK_EQ(2, cons_profile.f_count()); | |
71 } | |
72 | |
73 | |
74 static JSObjectsCluster AddHeapObjectToTree(JSObjectsRetainerTree* tree, | |
75 i::String* constructor, | |
76 int instance, | |
77 JSObjectsCluster* ref1 = NULL, | |
78 JSObjectsCluster* ref2 = NULL, | |
79 JSObjectsCluster* ref3 = NULL) { | |
80 JSObjectsCluster o(constructor, reinterpret_cast<i::Object*>(instance)); | |
81 JSObjectsClusterTree* o_tree = new JSObjectsClusterTree(); | |
82 JSObjectsClusterTree::Locator o_loc; | |
83 if (ref1 != NULL) o_tree->Insert(*ref1, &o_loc); | |
84 if (ref2 != NULL) o_tree->Insert(*ref2, &o_loc); | |
85 if (ref3 != NULL) o_tree->Insert(*ref3, &o_loc); | |
86 JSObjectsRetainerTree::Locator loc; | |
87 tree->Insert(o, &loc); | |
88 loc.set_value(o_tree); | |
89 return o; | |
90 } | |
91 | |
92 | |
93 static void AddSelfReferenceToTree(JSObjectsRetainerTree* tree, | |
94 JSObjectsCluster* self_ref) { | |
95 JSObjectsRetainerTree::Locator loc; | |
96 CHECK(tree->Find(*self_ref, &loc)); | |
97 JSObjectsClusterTree::Locator o_loc; | |
98 CHECK_NE(NULL, loc.value()); | |
99 loc.value()->Insert(*self_ref, &o_loc); | |
100 } | |
101 | |
102 | |
103 static inline void CheckEqualsHelper(const char* file, int line, | |
104 const char* expected_source, | |
105 const JSObjectsCluster& expected, | |
106 const char* value_source, | |
107 const JSObjectsCluster& value) { | |
108 if (JSObjectsCluster::Compare(expected, value) != 0) { | |
109 i::HeapStringAllocator allocator; | |
110 i::StringStream stream(&allocator); | |
111 stream.Add("# Expected: "); | |
112 expected.DebugPrint(&stream); | |
113 stream.Add("\n# Found: "); | |
114 value.DebugPrint(&stream); | |
115 V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n%s", | |
116 expected_source, value_source, | |
117 *stream.ToCString()); | |
118 } | |
119 } | |
120 | |
121 | |
122 static inline void CheckNonEqualsHelper(const char* file, int line, | |
123 const char* expected_source, | |
124 const JSObjectsCluster& expected, | |
125 const char* value_source, | |
126 const JSObjectsCluster& value) { | |
127 if (JSObjectsCluster::Compare(expected, value) == 0) { | |
128 i::HeapStringAllocator allocator; | |
129 i::StringStream stream(&allocator); | |
130 stream.Add("# !Expected: "); | |
131 expected.DebugPrint(&stream); | |
132 stream.Add("\n# Found: "); | |
133 value.DebugPrint(&stream); | |
134 V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n%s", | |
135 expected_source, value_source, | |
136 *stream.ToCString()); | |
137 } | |
138 } | |
139 | |
140 | |
141 TEST(ClustersCoarserSimple) { | |
142 v8::HandleScope scope; | |
143 LocalContext env; | |
144 | |
145 i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); | |
146 | |
147 JSObjectsRetainerTree tree; | |
148 JSObjectsCluster function(HEAP->function_class_symbol()); | |
149 JSObjectsCluster a(*FACTORY->NewStringFromAscii(i::CStrVector("A"))); | |
150 JSObjectsCluster b(*FACTORY->NewStringFromAscii(i::CStrVector("B"))); | |
151 | |
152 // o1 <- Function | |
153 JSObjectsCluster o1 = | |
154 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100, &function); | |
155 // o2 <- Function | |
156 JSObjectsCluster o2 = | |
157 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x200, &function); | |
158 // o3 <- A, B | |
159 JSObjectsCluster o3 = | |
160 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &a, &b); | |
161 // o4 <- B, A | |
162 JSObjectsCluster o4 = | |
163 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x400, &b, &a); | |
164 // o5 <- A, B, Function | |
165 JSObjectsCluster o5 = | |
166 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x500, | |
167 &a, &b, &function); | |
168 | |
169 ClustersCoarser coarser; | |
170 coarser.Process(&tree); | |
171 | |
172 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); | |
173 CHECK_EQ(coarser.GetCoarseEquivalent(o3), coarser.GetCoarseEquivalent(o4)); | |
174 CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o3)); | |
175 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o5)); | |
176 } | |
177 | |
178 | |
179 TEST(ClustersCoarserMultipleConstructors) { | |
180 v8::HandleScope scope; | |
181 LocalContext env; | |
182 | |
183 i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); | |
184 | |
185 JSObjectsRetainerTree tree; | |
186 JSObjectsCluster function(HEAP->function_class_symbol()); | |
187 | |
188 // o1 <- Function | |
189 JSObjectsCluster o1 = | |
190 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100, &function); | |
191 // a1 <- Function | |
192 JSObjectsCluster a1 = | |
193 AddHeapObjectToTree(&tree, HEAP->Array_symbol(), 0x1000, &function); | |
194 // o2 <- Function | |
195 JSObjectsCluster o2 = | |
196 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x200, &function); | |
197 // a2 <- Function | |
198 JSObjectsCluster a2 = | |
199 AddHeapObjectToTree(&tree, HEAP->Array_symbol(), 0x2000, &function); | |
200 | |
201 ClustersCoarser coarser; | |
202 coarser.Process(&tree); | |
203 | |
204 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); | |
205 CHECK_EQ(coarser.GetCoarseEquivalent(a1), coarser.GetCoarseEquivalent(a2)); | |
206 } | |
207 | |
208 | |
209 TEST(ClustersCoarserPathsTraversal) { | |
210 v8::HandleScope scope; | |
211 LocalContext env; | |
212 | |
213 i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); | |
214 | |
215 JSObjectsRetainerTree tree; | |
216 | |
217 // On the following graph: | |
218 // | |
219 // p | |
220 // <- o21 <- o11 <- | |
221 // q o | |
222 // <- o22 <- o12 <- | |
223 // r | |
224 // | |
225 // we expect that coarser will deduce equivalences: p ~ q ~ r, | |
226 // o21 ~ o22, and o11 ~ o12. | |
227 | |
228 JSObjectsCluster o = | |
229 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100); | |
230 JSObjectsCluster o11 = | |
231 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x110, &o); | |
232 JSObjectsCluster o12 = | |
233 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x120, &o); | |
234 JSObjectsCluster o21 = | |
235 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x210, &o11); | |
236 JSObjectsCluster o22 = | |
237 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x220, &o12); | |
238 JSObjectsCluster p = | |
239 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &o21); | |
240 JSObjectsCluster q = | |
241 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x310, &o21, &o22); | |
242 JSObjectsCluster r = | |
243 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x320, &o22); | |
244 | |
245 ClustersCoarser coarser; | |
246 coarser.Process(&tree); | |
247 | |
248 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o)); | |
249 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o11)); | |
250 CHECK_EQ(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o12)); | |
251 CHECK_EQ(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(o22)); | |
252 CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o21)); | |
253 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p)); | |
254 CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); | |
255 CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); | |
256 CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(p)); | |
257 CHECK_NE(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(p)); | |
258 } | |
259 | |
260 | |
261 TEST(ClustersCoarserSelf) { | |
262 v8::HandleScope scope; | |
263 LocalContext env; | |
264 | |
265 i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); | |
266 | |
267 JSObjectsRetainerTree tree; | |
268 | |
269 // On the following graph: | |
270 // | |
271 // p (self-referencing) | |
272 // <- o1 <- | |
273 // q (self-referencing) o | |
274 // <- o2 <- | |
275 // r (self-referencing) | |
276 // | |
277 // we expect that coarser will deduce equivalences: p ~ q ~ r, o1 ~ o2; | |
278 | |
279 JSObjectsCluster o = | |
280 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100); | |
281 JSObjectsCluster o1 = | |
282 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x110, &o); | |
283 JSObjectsCluster o2 = | |
284 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x120, &o); | |
285 JSObjectsCluster p = | |
286 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &o1); | |
287 AddSelfReferenceToTree(&tree, &p); | |
288 JSObjectsCluster q = | |
289 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x310, &o1, &o2); | |
290 AddSelfReferenceToTree(&tree, &q); | |
291 JSObjectsCluster r = | |
292 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x320, &o2); | |
293 AddSelfReferenceToTree(&tree, &r); | |
294 | |
295 ClustersCoarser coarser; | |
296 coarser.Process(&tree); | |
297 | |
298 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o)); | |
299 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o1)); | |
300 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); | |
301 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p)); | |
302 CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); | |
303 CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); | |
304 CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(p)); | |
305 } | |
306 | |
307 | |
308 namespace { | |
309 | |
310 class RetainerProfilePrinter : public RetainerHeapProfile::Printer { | |
311 public: | |
312 RetainerProfilePrinter() : stream_(&allocator_), lines_(100) {} | |
313 | |
314 void PrintRetainers(const JSObjectsCluster& cluster, | |
315 const i::StringStream& retainers) { | |
316 cluster.Print(&stream_); | |
317 stream_.Add("%s", *(retainers.ToCString())); | |
318 stream_.Put('\0'); | |
319 } | |
320 | |
321 const char* GetRetainers(const char* constructor) { | |
322 FillLines(); | |
323 const size_t cons_len = strlen(constructor); | |
324 for (int i = 0; i < lines_.length(); ++i) { | |
325 if (strncmp(constructor, lines_[i], cons_len) == 0 && | |
326 lines_[i][cons_len] == ',') { | |
327 return lines_[i] + cons_len + 1; | |
328 } | |
329 } | |
330 return NULL; | |
331 } | |
332 | |
333 private: | |
334 void FillLines() { | |
335 if (lines_.length() > 0) return; | |
336 stream_.Put('\0'); | |
337 stream_str_ = stream_.ToCString(); | |
338 const char* pos = *stream_str_; | |
339 while (pos != NULL && *pos != '\0') { | |
340 lines_.Add(pos); | |
341 pos = strchr(pos, '\0'); | |
342 if (pos != NULL) ++pos; | |
343 } | |
344 } | |
345 | |
346 i::HeapStringAllocator allocator_; | |
347 i::StringStream stream_; | |
348 i::SmartPointer<const char> stream_str_; | |
349 i::List<const char*> lines_; | |
350 }; | |
351 | |
352 } // namespace | |
353 | |
354 | |
355 TEST(RetainerProfile) { | |
356 v8::HandleScope scope; | |
357 LocalContext env; | |
358 | |
359 CompileRun( | |
360 "function A() {}\n" | |
361 "function B(x) { this.x = x; }\n" | |
362 "function C(x) { this.x1 = x; this.x2 = x; }\n" | |
363 "var a = new A();\n" | |
364 "var b1 = new B(a), b2 = new B(a);\n" | |
365 "var c = new C(a);"); | |
366 | |
367 RetainerHeapProfile ret_profile; | |
368 i::AssertNoAllocation no_alloc; | |
369 i::HeapIterator iterator; | |
370 for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) | |
371 ret_profile.CollectStats(obj); | |
372 ret_profile.CoarseAndAggregate(); | |
373 RetainerProfilePrinter printer; | |
374 ret_profile.DebugPrintStats(&printer); | |
375 const char* retainers_of_a = printer.GetRetainers("A"); | |
376 // The order of retainers is unspecified, so we check string length, and | |
377 // verify each retainer separately. | |
378 CHECK_EQ(i::StrLength("(global property);1,B;2,C;2"), | |
379 i::StrLength(retainers_of_a)); | |
380 CHECK(strstr(retainers_of_a, "(global property);1") != NULL); | |
381 CHECK(strstr(retainers_of_a, "B;2") != NULL); | |
382 CHECK(strstr(retainers_of_a, "C;2") != NULL); | |
383 CHECK_EQ("(global property);2", printer.GetRetainers("B")); | |
384 CHECK_EQ("(global property);1", printer.GetRetainers("C")); | |
385 } | |
386 | |
387 | |
388 namespace { | |
389 | |
390 class NamedEntriesDetector { | 19 class NamedEntriesDetector { |
391 public: | 20 public: |
392 NamedEntriesDetector() | 21 NamedEntriesDetector() |
393 : has_A2(false), has_B2(false), has_C2(false) { | 22 : has_A2(false), has_B2(false), has_C2(false) { |
394 } | 23 } |
395 | 24 |
396 void Apply(i::HeapEntry** entry_ptr) { | 25 void Apply(i::HeapEntry** entry_ptr) { |
397 if (IsReachableNodeWithName(*entry_ptr, "A2")) has_A2 = true; | 26 if (IsReachableNodeWithName(*entry_ptr, "A2")) has_A2 = true; |
398 if (IsReachableNodeWithName(*entry_ptr, "B2")) has_B2 = true; | 27 if (IsReachableNodeWithName(*entry_ptr, "B2")) has_B2 = true; |
399 if (IsReachableNodeWithName(*entry_ptr, "C2")) has_C2 = true; | 28 if (IsReachableNodeWithName(*entry_ptr, "C2")) has_C2 = true; |
(...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
719 const v8::HeapSnapshot* snapshot = | 348 const v8::HeapSnapshot* snapshot = |
720 v8::HeapProfiler::TakeSnapshot(v8::String::New("s")); | 349 v8::HeapProfiler::TakeSnapshot(v8::String::New("s")); |
721 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); | 350 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); |
722 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( | 351 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( |
723 snapshot))->GetSortedEntriesList(); | 352 snapshot))->GetSortedEntriesList(); |
724 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); | 353 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); |
725 CHECK_EQ(root1, root2); | 354 CHECK_EQ(root1, root2); |
726 } | 355 } |
727 | 356 |
728 | 357 |
729 static const v8::HeapGraphNode* GetChild( | |
730 const v8::HeapGraphNode* node, | |
731 v8::HeapGraphNode::Type type, | |
732 const char* name, | |
733 const v8::HeapGraphNode* after = NULL) { | |
734 bool ignore_child = after == NULL ? false : true; | |
735 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { | |
736 const v8::HeapGraphEdge* prop = node->GetChild(i); | |
737 const v8::HeapGraphNode* child = prop->GetToNode(); | |
738 v8::String::AsciiValue child_name(child->GetName()); | |
739 if (!ignore_child | |
740 && child->GetType() == type | |
741 && strcmp(name, *child_name) == 0) | |
742 return child; | |
743 if (after != NULL && child == after) ignore_child = false; | |
744 } | |
745 return NULL; | |
746 } | |
747 | |
748 static bool IsNodeRetainedAs(const v8::HeapGraphNode* node, | |
749 int element) { | |
750 for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) { | |
751 const v8::HeapGraphEdge* prop = node->GetRetainer(i); | |
752 if (prop->GetType() == v8::HeapGraphEdge::kElement | |
753 && element == prop->GetName()->Int32Value()) | |
754 return true; | |
755 } | |
756 return false; | |
757 } | |
758 | |
759 TEST(AggregatedHeapSnapshot) { | |
760 v8::HandleScope scope; | |
761 LocalContext env; | |
762 | |
763 CompileRun( | |
764 "function A() {}\n" | |
765 "function B(x) { this.x = x; }\n" | |
766 "var a = new A();\n" | |
767 "var b = new B(a);"); | |
768 const v8::HeapSnapshot* snapshot = | |
769 v8::HeapProfiler::TakeSnapshot( | |
770 v8::String::New("agg"), v8::HeapSnapshot::kAggregated); | |
771 const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(), | |
772 v8::HeapGraphNode::kHidden, | |
773 "STRING_TYPE"); | |
774 CHECK_NE(NULL, strings); | |
775 CHECK_NE(0, strings->GetSelfSize()); | |
776 CHECK_NE(0, strings->GetInstancesCount()); | |
777 const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(), | |
778 v8::HeapGraphNode::kHidden, | |
779 "MAP_TYPE"); | |
780 CHECK_NE(NULL, maps); | |
781 CHECK_NE(0, maps->GetSelfSize()); | |
782 CHECK_NE(0, maps->GetInstancesCount()); | |
783 | |
784 const v8::HeapGraphNode* a = GetChild(snapshot->GetRoot(), | |
785 v8::HeapGraphNode::kObject, | |
786 "A"); | |
787 CHECK_NE(NULL, a); | |
788 CHECK_NE(0, a->GetSelfSize()); | |
789 CHECK_EQ(1, a->GetInstancesCount()); | |
790 | |
791 const v8::HeapGraphNode* b = GetChild(snapshot->GetRoot(), | |
792 v8::HeapGraphNode::kObject, | |
793 "B"); | |
794 CHECK_NE(NULL, b); | |
795 CHECK_NE(0, b->GetSelfSize()); | |
796 CHECK_EQ(1, b->GetInstancesCount()); | |
797 | |
798 const v8::HeapGraphNode* glob_prop = GetChild(snapshot->GetRoot(), | |
799 v8::HeapGraphNode::kObject, | |
800 "(global property)", | |
801 b); | |
802 CHECK_NE(NULL, glob_prop); | |
803 CHECK_EQ(0, glob_prop->GetSelfSize()); | |
804 CHECK_EQ(0, glob_prop->GetInstancesCount()); | |
805 CHECK_NE(0, glob_prop->GetChildrenCount()); | |
806 | |
807 const v8::HeapGraphNode* a_from_glob_prop = GetChild( | |
808 glob_prop, | |
809 v8::HeapGraphNode::kObject, | |
810 "A"); | |
811 CHECK_NE(NULL, a_from_glob_prop); | |
812 CHECK_EQ(0, a_from_glob_prop->GetSelfSize()); | |
813 CHECK_EQ(0, a_from_glob_prop->GetInstancesCount()); | |
814 CHECK_EQ(0, a_from_glob_prop->GetChildrenCount()); // Retains nothing. | |
815 CHECK(IsNodeRetainedAs(a_from_glob_prop, 1)); // (global propery) has 1 ref. | |
816 | |
817 const v8::HeapGraphNode* b_with_children = GetChild( | |
818 snapshot->GetRoot(), | |
819 v8::HeapGraphNode::kObject, | |
820 "B", | |
821 b); | |
822 CHECK_NE(NULL, b_with_children); | |
823 CHECK_EQ(0, b_with_children->GetSelfSize()); | |
824 CHECK_EQ(0, b_with_children->GetInstancesCount()); | |
825 CHECK_NE(0, b_with_children->GetChildrenCount()); | |
826 | |
827 const v8::HeapGraphNode* a_from_b = GetChild( | |
828 b_with_children, | |
829 v8::HeapGraphNode::kObject, | |
830 "A"); | |
831 CHECK_NE(NULL, a_from_b); | |
832 CHECK_EQ(0, a_from_b->GetSelfSize()); | |
833 CHECK_EQ(0, a_from_b->GetInstancesCount()); | |
834 CHECK_EQ(0, a_from_b->GetChildrenCount()); // Retains nothing. | |
835 CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A. | |
836 } | |
837 | |
838 | |
839 TEST(HeapEntryDominator) { | 358 TEST(HeapEntryDominator) { |
840 // The graph looks like this: | 359 // The graph looks like this: |
841 // | 360 // |
842 // -> node1 | 361 // -> node1 |
843 // a |^ | 362 // a |^ |
844 // -> node5 ba | 363 // -> node5 ba |
845 // a v| | 364 // a v| |
846 // node6 -> node2 | 365 // node6 -> node2 |
847 // b a |^ | 366 // b a |^ |
848 // -> node4 ba | 367 // -> node4 ba |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1041 LocalContext env; | 560 LocalContext env; |
1042 const v8::HeapSnapshot* snapshot = | 561 const v8::HeapSnapshot* snapshot = |
1043 v8::HeapProfiler::TakeSnapshot(v8::String::New("abort")); | 562 v8::HeapProfiler::TakeSnapshot(v8::String::New("abort")); |
1044 TestJSONStream stream(5); | 563 TestJSONStream stream(5); |
1045 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); | 564 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); |
1046 CHECK_GT(stream.size(), 0); | 565 CHECK_GT(stream.size(), 0); |
1047 CHECK_EQ(0, stream.eos_signaled()); | 566 CHECK_EQ(0, stream.eos_signaled()); |
1048 } | 567 } |
1049 | 568 |
1050 | 569 |
1051 // Must not crash in debug mode. | |
1052 TEST(AggregatedHeapSnapshotJSONSerialization) { | |
1053 v8::HandleScope scope; | |
1054 LocalContext env; | |
1055 | |
1056 const v8::HeapSnapshot* snapshot = | |
1057 v8::HeapProfiler::TakeSnapshot( | |
1058 v8::String::New("agg"), v8::HeapSnapshot::kAggregated); | |
1059 TestJSONStream stream; | |
1060 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); | |
1061 CHECK_GT(stream.size(), 0); | |
1062 CHECK_EQ(1, stream.eos_signaled()); | |
1063 } | |
1064 | |
1065 | |
1066 TEST(HeapSnapshotGetNodeById) { | 570 TEST(HeapSnapshotGetNodeById) { |
1067 v8::HandleScope scope; | 571 v8::HandleScope scope; |
1068 LocalContext env; | 572 LocalContext env; |
1069 | 573 |
1070 const v8::HeapSnapshot* snapshot = | 574 const v8::HeapSnapshot* snapshot = |
1071 v8::HeapProfiler::TakeSnapshot(v8::String::New("id")); | 575 v8::HeapProfiler::TakeSnapshot(v8::String::New("id")); |
1072 const v8::HeapGraphNode* root = snapshot->GetRoot(); | 576 const v8::HeapGraphNode* root = snapshot->GetRoot(); |
1073 CHECK_EQ(root, snapshot->GetNodeById(root->GetId())); | 577 CHECK_EQ(root, snapshot->GetNodeById(root->GetId())); |
1074 for (int i = 0, count = root->GetChildrenCount(); i < count; ++i) { | 578 for (int i = 0, count = root->GetChildrenCount(); i < count; ++i) { |
1075 const v8::HeapGraphEdge* prop = root->GetChild(i); | 579 const v8::HeapGraphEdge* prop = root->GetChild(i); |
(...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1384 const int nodes_count = snapshot->GetNodesCount(); | 888 const int nodes_count = snapshot->GetNodesCount(); |
1385 int count = 0; | 889 int count = 0; |
1386 for (int i = 0; i < nodes_count; ++i) { | 890 for (int i = 0; i < nodes_count; ++i) { |
1387 if (snapshot->GetNode(i) == global) | 891 if (snapshot->GetNode(i) == global) |
1388 ++count; | 892 ++count; |
1389 } | 893 } |
1390 CHECK_EQ(1, count); | 894 CHECK_EQ(1, count); |
1391 } | 895 } |
1392 | 896 |
1393 #endif // ENABLE_LOGGING_AND_PROFILING | 897 #endif // ENABLE_LOGGING_AND_PROFILING |
OLD | NEW |