OLD | NEW |
1 // Copyright 2009 the V8 project authors. All rights reserved. | 1 // Copyright 2009 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 19 matching lines...) Expand all Loading... |
30 #include "heap-profiler.h" | 30 #include "heap-profiler.h" |
31 #include "string-stream.h" | 31 #include "string-stream.h" |
32 | 32 |
33 namespace v8 { | 33 namespace v8 { |
34 namespace internal { | 34 namespace internal { |
35 | 35 |
36 | 36 |
37 #ifdef ENABLE_LOGGING_AND_PROFILING | 37 #ifdef ENABLE_LOGGING_AND_PROFILING |
38 namespace { | 38 namespace { |
39 | 39 |
40 // JSStatsHelper provides service functions for examining | 40 // Clusterizer is a set of helper functions for converting |
41 // JS objects allocated on heap. It is run during garbage | 41 // object references into clusters. |
42 // collection cycle, thus it doesn't need to use handles. | 42 class Clusterizer : public AllStatic { |
43 class JSStatsHelper { | |
44 public: | 43 public: |
| 44 static JSObjectsCluster Clusterize(HeapObject* obj) { |
| 45 return Clusterize(obj, true); |
| 46 } |
| 47 static void InsertIntoTree(JSObjectsClusterTree* tree, |
| 48 HeapObject* obj, bool fine_grain); |
| 49 static void InsertReferenceIntoTree(JSObjectsClusterTree* tree, |
| 50 const JSObjectsCluster& cluster) { |
| 51 InsertIntoTree(tree, cluster, 0); |
| 52 } |
| 53 |
| 54 private: |
| 55 static JSObjectsCluster Clusterize(HeapObject* obj, bool fine_grain); |
45 static int CalculateNetworkSize(JSObject* obj); | 56 static int CalculateNetworkSize(JSObject* obj); |
46 private: | 57 static int GetObjectSize(HeapObject* obj) { |
47 DISALLOW_IMPLICIT_CONSTRUCTORS(JSStatsHelper); | 58 return obj->IsJSObject() ? |
| 59 CalculateNetworkSize(JSObject::cast(obj)) : obj->Size(); |
| 60 } |
| 61 static void InsertIntoTree(JSObjectsClusterTree* tree, |
| 62 const JSObjectsCluster& cluster, int size); |
48 }; | 63 }; |
49 | 64 |
50 | 65 |
51 int JSStatsHelper::CalculateNetworkSize(JSObject* obj) { | 66 JSObjectsCluster Clusterizer::Clusterize(HeapObject* obj, bool fine_grain) { |
| 67 if (obj->IsJSObject()) { |
| 68 JSObject* js_obj = JSObject::cast(obj); |
| 69 String* constructor = JSObject::cast(js_obj)->constructor_name(); |
| 70 // Differentiate Object and Array instances. |
| 71 if (fine_grain && (constructor == Heap::Object_symbol() || |
| 72 constructor == Heap::Array_symbol())) { |
| 73 return JSObjectsCluster(constructor, obj); |
| 74 } else { |
| 75 return JSObjectsCluster(constructor); |
| 76 } |
| 77 } else if (obj->IsString()) { |
| 78 return JSObjectsCluster(Heap::String_symbol()); |
| 79 } |
| 80 return JSObjectsCluster(); |
| 81 } |
| 82 |
| 83 |
| 84 void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree, |
| 85 HeapObject* obj, bool fine_grain) { |
| 86 JSObjectsCluster cluster = Clusterize(obj, fine_grain); |
| 87 if (cluster.is_null()) return; |
| 88 InsertIntoTree(tree, cluster, GetObjectSize(obj)); |
| 89 } |
| 90 |
| 91 |
| 92 void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree, |
| 93 const JSObjectsCluster& cluster, int size) { |
| 94 JSObjectsClusterTree::Locator loc; |
| 95 tree->Insert(cluster, &loc); |
| 96 NumberAndSizeInfo number_and_size = loc.value(); |
| 97 number_and_size.increment_number(1); |
| 98 number_and_size.increment_bytes(size); |
| 99 loc.set_value(number_and_size); |
| 100 } |
| 101 |
| 102 |
| 103 int Clusterizer::CalculateNetworkSize(JSObject* obj) { |
52 int size = obj->Size(); | 104 int size = obj->Size(); |
53 // If 'properties' and 'elements' are non-empty (thus, non-shared), | 105 // If 'properties' and 'elements' are non-empty (thus, non-shared), |
54 // take their size into account. | 106 // take their size into account. |
55 if (FixedArray::cast(obj->properties())->length() != 0) { | 107 if (FixedArray::cast(obj->properties())->length() != 0) { |
56 size += obj->properties()->Size(); | 108 size += obj->properties()->Size(); |
57 } | 109 } |
58 if (FixedArray::cast(obj->elements())->length() != 0) { | 110 if (FixedArray::cast(obj->elements())->length() != 0) { |
59 size += obj->elements()->Size(); | 111 size += obj->elements()->Size(); |
60 } | 112 } |
61 return size; | 113 return size; |
62 } | 114 } |
63 | 115 |
64 | 116 |
65 // A helper class for recording back references. | 117 // A helper class for recording back references. |
66 class ReferencesExtractor : public ObjectVisitor { | 118 class ReferencesExtractor : public ObjectVisitor { |
67 public: | 119 public: |
68 ReferencesExtractor( | 120 ReferencesExtractor(const JSObjectsCluster& cluster, |
69 const JSObjectsCluster& cluster, RetainerHeapProfile* profile) | 121 RetainerHeapProfile* profile) |
70 : cluster_(cluster), | 122 : cluster_(cluster), |
71 profile_(profile), | 123 profile_(profile), |
72 inside_array_(false) { | 124 inside_array_(false) { |
73 } | 125 } |
74 | 126 |
75 void VisitPointer(Object** o) { | 127 void VisitPointer(Object** o) { |
76 if ((*o)->IsJSObject() || (*o)->IsString()) { | 128 if ((*o)->IsJSObject() || (*o)->IsString()) { |
77 profile_->StoreReference(cluster_, *o); | 129 profile_->StoreReference(cluster_, HeapObject::cast(*o)); |
78 } else if ((*o)->IsFixedArray() && !inside_array_) { | 130 } else if ((*o)->IsFixedArray() && !inside_array_) { |
79 // Traverse one level deep for data members that are fixed arrays. | 131 // Traverse one level deep for data members that are fixed arrays. |
80 // This covers the case of 'elements' and 'properties' of JSObject, | 132 // This covers the case of 'elements' and 'properties' of JSObject, |
81 // and function contexts. | 133 // and function contexts. |
82 inside_array_ = true; | 134 inside_array_ = true; |
83 FixedArray::cast(*o)->Iterate(this); | 135 FixedArray::cast(*o)->Iterate(this); |
84 inside_array_ = false; | 136 inside_array_ = false; |
85 } | 137 } |
86 } | 138 } |
87 | 139 |
88 void VisitPointers(Object** start, Object** end) { | 140 void VisitPointers(Object** start, Object** end) { |
89 for (Object** p = start; p < end; p++) VisitPointer(p); | 141 for (Object** p = start; p < end; p++) VisitPointer(p); |
90 } | 142 } |
91 | 143 |
92 private: | 144 private: |
93 const JSObjectsCluster& cluster_; | 145 const JSObjectsCluster& cluster_; |
94 RetainerHeapProfile* profile_; | 146 RetainerHeapProfile* profile_; |
95 bool inside_array_; | 147 bool inside_array_; |
96 }; | 148 }; |
97 | 149 |
98 | 150 |
99 // A printer interface implementation for the Retainers profile. | 151 // A printer interface implementation for the Retainers profile. |
100 class RetainersPrinter : public RetainerHeapProfile::Printer { | 152 class RetainersPrinter : public RetainerHeapProfile::Printer { |
101 public: | 153 public: |
102 void PrintRetainers(const StringStream& retainers) { | 154 void PrintRetainers(const JSObjectsCluster& cluster, |
103 LOG(HeapSampleJSRetainersEvent(*(retainers.ToCString()))); | 155 const StringStream& retainers) { |
| 156 HeapStringAllocator allocator; |
| 157 StringStream stream(&allocator); |
| 158 cluster.Print(&stream); |
| 159 LOG(HeapSampleJSRetainersEvent( |
| 160 *(stream.ToCString()), *(retainers.ToCString()))); |
104 } | 161 } |
105 }; | 162 }; |
106 | 163 |
| 164 |
| 165 class RetainerTreePrinter BASE_EMBEDDED { |
| 166 public: |
| 167 explicit RetainerTreePrinter(StringStream* stream) : stream_(stream) {} |
| 168 void Call(const JSObjectsCluster& cluster, |
| 169 const NumberAndSizeInfo& number_and_size) { |
| 170 Print(stream_, cluster, number_and_size); |
| 171 } |
| 172 static void Print(StringStream* stream, |
| 173 const JSObjectsCluster& cluster, |
| 174 const NumberAndSizeInfo& numNNber_and_size); |
| 175 |
| 176 private: |
| 177 StringStream* stream_; |
| 178 }; |
| 179 |
| 180 |
| 181 void RetainerTreePrinter::Print(StringStream* stream, |
| 182 const JSObjectsCluster& cluster, |
| 183 const NumberAndSizeInfo& number_and_size) { |
| 184 stream->Put(','); |
| 185 cluster.Print(stream); |
| 186 stream->Add(";%d", number_and_size.number()); |
| 187 } |
| 188 |
| 189 |
107 } // namespace | 190 } // namespace |
108 | 191 |
109 | 192 |
110 const ConstructorHeapProfile::TreeConfig::Key | 193 const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey; |
111 ConstructorHeapProfile::TreeConfig::kNoKey = NULL; | 194 const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue; |
112 const ConstructorHeapProfile::TreeConfig::Value | |
113 ConstructorHeapProfile::TreeConfig::kNoValue; | |
114 | 195 |
115 | 196 |
116 ConstructorHeapProfile::ConstructorHeapProfile() | 197 ConstructorHeapProfile::ConstructorHeapProfile() |
117 : zscope_(DELETE_ON_EXIT) { | 198 : zscope_(DELETE_ON_EXIT) { |
118 } | 199 } |
119 | 200 |
120 | 201 |
121 void ConstructorHeapProfile::Call(String* name, | 202 void ConstructorHeapProfile::Call(const JSObjectsCluster& cluster, |
122 const NumberAndSizeInfo& number_and_size) { | 203 const NumberAndSizeInfo& number_and_size) { |
123 ASSERT(name != NULL); | 204 HeapStringAllocator allocator; |
124 SmartPointer<char> s_name( | 205 StringStream stream(&allocator); |
125 name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)); | 206 cluster.Print(&stream); |
126 LOG(HeapSampleJSConstructorEvent(*s_name, | 207 LOG(HeapSampleJSConstructorEvent(*(stream.ToCString()), |
127 number_and_size.number(), | 208 number_and_size.number(), |
128 number_and_size.bytes())); | 209 number_and_size.bytes())); |
129 } | 210 } |
130 | 211 |
131 | 212 |
132 void ConstructorHeapProfile::CollectStats(HeapObject* obj) { | 213 void ConstructorHeapProfile::CollectStats(HeapObject* obj) { |
133 String* constructor = NULL; | 214 Clusterizer::InsertIntoTree(&js_objects_info_tree_, obj, false); |
134 int size; | |
135 if (obj->IsString()) { | |
136 constructor = Heap::String_symbol(); | |
137 size = obj->Size(); | |
138 } else if (obj->IsJSObject()) { | |
139 JSObject* js_obj = JSObject::cast(obj); | |
140 constructor = js_obj->constructor_name(); | |
141 size = JSStatsHelper::CalculateNetworkSize(js_obj); | |
142 } else { | |
143 return; | |
144 } | |
145 | |
146 JSObjectsInfoTree::Locator loc; | |
147 if (!js_objects_info_tree_.Find(constructor, &loc)) { | |
148 js_objects_info_tree_.Insert(constructor, &loc); | |
149 } | |
150 NumberAndSizeInfo number_and_size = loc.value(); | |
151 number_and_size.increment_number(1); | |
152 number_and_size.increment_bytes(size); | |
153 loc.set_value(number_and_size); | |
154 } | 215 } |
155 | 216 |
156 | 217 |
157 void ConstructorHeapProfile::PrintStats() { | 218 void ConstructorHeapProfile::PrintStats() { |
158 js_objects_info_tree_.ForEach(this); | 219 js_objects_info_tree_.ForEach(this); |
159 } | 220 } |
160 | 221 |
161 | 222 |
162 void JSObjectsCluster::Print(StringStream* accumulator) const { | 223 void JSObjectsCluster::Print(StringStream* accumulator) const { |
163 ASSERT(!is_null()); | 224 ASSERT(!is_null()); |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
224 } | 285 } |
225 | 286 |
226 | 287 |
227 ClustersCoarser::ClustersCoarser() | 288 ClustersCoarser::ClustersCoarser() |
228 : zscope_(DELETE_ON_EXIT), | 289 : zscope_(DELETE_ON_EXIT), |
229 sim_list_(ClustersCoarser::kInitialSimilarityListCapacity), | 290 sim_list_(ClustersCoarser::kInitialSimilarityListCapacity), |
230 current_pair_(NULL) { | 291 current_pair_(NULL) { |
231 } | 292 } |
232 | 293 |
233 | 294 |
234 void ClustersCoarser::Call( | 295 void ClustersCoarser::Call(const JSObjectsCluster& cluster, |
235 const JSObjectsCluster& cluster, JSObjectsClusterTree* tree) { | 296 JSObjectsClusterTree* tree) { |
236 if (tree != NULL) { | 297 if (!cluster.can_be_coarsed()) return; |
237 // First level of retainer graph. | 298 ClusterBackRefs pair(cluster); |
238 if (!cluster.can_be_coarsed()) return; | 299 ASSERT(current_pair_ == NULL); |
239 ClusterBackRefs pair(cluster); | 300 current_pair_ = &pair; |
240 ASSERT(current_pair_ == NULL); | 301 current_set_ = new JSObjectsRetainerTree(); |
241 current_pair_ = &pair; | 302 tree->ForEach(this); |
242 current_set_ = new JSObjectsClusterTree(); | 303 sim_list_.Add(pair); |
243 tree->ForEach(this); | 304 current_pair_ = NULL; |
244 sim_list_.Add(pair); | 305 current_set_ = NULL; |
245 current_pair_ = NULL; | 306 } |
246 current_set_ = NULL; | 307 |
| 308 |
| 309 void ClustersCoarser::Call(const JSObjectsCluster& cluster, |
| 310 const NumberAndSizeInfo& number_and_size) { |
| 311 ASSERT(current_pair_ != NULL); |
| 312 ASSERT(current_set_ != NULL); |
| 313 JSObjectsCluster eq = GetCoarseEquivalent(cluster); |
| 314 JSObjectsRetainerTree::Locator loc; |
| 315 if (!eq.is_null()) { |
| 316 if (current_set_->Find(eq, &loc)) return; |
| 317 current_pair_->refs.Add(eq); |
| 318 current_set_->Insert(eq, &loc); |
247 } else { | 319 } else { |
248 // Second level of retainer graph. | 320 current_pair_->refs.Add(cluster); |
249 ASSERT(current_pair_ != NULL); | |
250 ASSERT(current_set_ != NULL); | |
251 JSObjectsCluster eq = GetCoarseEquivalent(cluster); | |
252 JSObjectsClusterTree::Locator loc; | |
253 if (!eq.is_null()) { | |
254 if (current_set_->Find(eq, &loc)) return; | |
255 current_pair_->refs.Add(eq); | |
256 current_set_->Insert(eq, &loc); | |
257 } else { | |
258 current_pair_->refs.Add(cluster); | |
259 } | |
260 } | 321 } |
261 } | 322 } |
262 | 323 |
263 | 324 |
264 void ClustersCoarser::Process(JSObjectsClusterTree* tree) { | 325 void ClustersCoarser::Process(JSObjectsRetainerTree* tree) { |
265 int last_eq_clusters = -1; | 326 int last_eq_clusters = -1; |
266 for (int i = 0; i < kMaxPassesCount; ++i) { | 327 for (int i = 0; i < kMaxPassesCount; ++i) { |
267 sim_list_.Clear(); | 328 sim_list_.Clear(); |
268 const int curr_eq_clusters = DoProcess(tree); | 329 const int curr_eq_clusters = DoProcess(tree); |
269 // If no new cluster equivalents discovered, abort processing. | 330 // If no new cluster equivalents discovered, abort processing. |
270 if (last_eq_clusters == curr_eq_clusters) break; | 331 if (last_eq_clusters == curr_eq_clusters) break; |
271 last_eq_clusters = curr_eq_clusters; | 332 last_eq_clusters = curr_eq_clusters; |
272 } | 333 } |
273 } | 334 } |
274 | 335 |
275 | 336 |
276 int ClustersCoarser::DoProcess(JSObjectsClusterTree* tree) { | 337 int ClustersCoarser::DoProcess(JSObjectsRetainerTree* tree) { |
277 tree->ForEach(this); | 338 tree->ForEach(this); |
278 // To sort similarity list properly, references list of a cluster is | 339 // To sort similarity list properly, references list of a cluster is |
279 // required to be sorted, thus 'O1 <- A, B' and 'O2 <- B, A' would | 340 // required to be sorted, thus 'O1 <- A, B' and 'O2 <- B, A' would |
280 // be considered equivalent. But we don't sort them explicitly | 341 // be considered equivalent. But we don't sort them explicitly |
281 // because we know that they come from a splay tree traversal, so | 342 // because we know that they come from a splay tree traversal, so |
282 // they are already sorted. | 343 // they are already sorted. |
283 sim_list_.Sort(ClusterBackRefsCmp); | 344 sim_list_.Sort(ClusterBackRefsCmp); |
284 return FillEqualityTree(); | 345 return FillEqualityTree(); |
285 } | 346 } |
286 | 347 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 eq_to = i; | 382 eq_to = i; |
322 first_added = false; | 383 first_added = false; |
323 } | 384 } |
324 } | 385 } |
325 return eq_clusters_count; | 386 return eq_clusters_count; |
326 } | 387 } |
327 | 388 |
328 | 389 |
329 const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoKey; | 390 const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoKey; |
330 const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoValue; | 391 const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoValue; |
331 const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey; | 392 const JSObjectsRetainerTreeConfig::Key JSObjectsRetainerTreeConfig::kNoKey; |
332 const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue = | 393 const JSObjectsRetainerTreeConfig::Value JSObjectsRetainerTreeConfig::kNoValue = |
333 NULL; | 394 NULL; |
334 | 395 |
335 | 396 |
336 RetainerHeapProfile::RetainerHeapProfile() | 397 RetainerHeapProfile::RetainerHeapProfile() |
337 : zscope_(DELETE_ON_EXIT), | 398 : zscope_(DELETE_ON_EXIT), |
338 coarse_cluster_tree_(NULL), | 399 coarse_cluster_tree_(NULL), |
339 retainers_printed_(0), | |
340 current_printer_(NULL), | 400 current_printer_(NULL), |
341 current_stream_(NULL) { | 401 current_stream_(NULL) { |
342 JSObjectsCluster roots(JSObjectsCluster::ROOTS); | 402 JSObjectsCluster roots(JSObjectsCluster::ROOTS); |
343 ReferencesExtractor extractor( | 403 ReferencesExtractor extractor(roots, this); |
344 roots, this); | |
345 Heap::IterateRoots(&extractor); | 404 Heap::IterateRoots(&extractor); |
346 } | 405 } |
347 | 406 |
348 | 407 |
349 JSObjectsCluster RetainerHeapProfile::Clusterize(Object* obj) { | 408 void RetainerHeapProfile::StoreReference(const JSObjectsCluster& cluster, |
350 if (obj->IsJSObject()) { | 409 HeapObject* ref) { |
351 String* constructor = JSObject::cast(obj)->constructor_name(); | 410 JSObjectsCluster ref_cluster = Clusterizer::Clusterize(ref); |
352 // Differentiate Object and Array instances. | 411 JSObjectsRetainerTree::Locator ref_loc; |
353 if (constructor == Heap::Object_symbol() || | |
354 constructor == Heap::Array_symbol()) { | |
355 return JSObjectsCluster(constructor, obj); | |
356 } else { | |
357 return JSObjectsCluster(constructor); | |
358 } | |
359 } else if (obj->IsString()) { | |
360 return JSObjectsCluster(Heap::String_symbol()); | |
361 } else { | |
362 UNREACHABLE(); | |
363 return JSObjectsCluster(); | |
364 } | |
365 } | |
366 | |
367 | |
368 void RetainerHeapProfile::StoreReference( | |
369 const JSObjectsCluster& cluster, | |
370 Object* ref) { | |
371 JSObjectsCluster ref_cluster = Clusterize(ref); | |
372 JSObjectsClusterTree::Locator ref_loc; | |
373 if (retainers_tree_.Insert(ref_cluster, &ref_loc)) { | 412 if (retainers_tree_.Insert(ref_cluster, &ref_loc)) { |
374 ref_loc.set_value(new JSObjectsClusterTree()); | 413 ref_loc.set_value(new JSObjectsClusterTree()); |
375 } | 414 } |
376 JSObjectsClusterTree* referenced_by = ref_loc.value(); | 415 JSObjectsClusterTree* referenced_by = ref_loc.value(); |
377 JSObjectsClusterTree::Locator obj_loc; | 416 Clusterizer::InsertReferenceIntoTree(referenced_by, cluster); |
378 referenced_by->Insert(cluster, &obj_loc); | |
379 } | 417 } |
380 | 418 |
381 | 419 |
382 void RetainerHeapProfile::CollectStats(HeapObject* obj) { | 420 void RetainerHeapProfile::CollectStats(HeapObject* obj) { |
383 if (obj->IsJSObject()) { | 421 if (obj->IsJSObject()) { |
384 const JSObjectsCluster cluster = Clusterize(JSObject::cast(obj)); | 422 const JSObjectsCluster cluster = Clusterizer::Clusterize(obj); |
385 ReferencesExtractor extractor(cluster, this); | 423 ReferencesExtractor extractor(cluster, this); |
386 obj->Iterate(&extractor); | 424 obj->Iterate(&extractor); |
387 } else if (obj->IsJSGlobalPropertyCell()) { | 425 } else if (obj->IsJSGlobalPropertyCell()) { |
388 JSObjectsCluster global_prop(JSObjectsCluster::GLOBAL_PROPERTY); | 426 JSObjectsCluster global_prop(JSObjectsCluster::GLOBAL_PROPERTY); |
389 ReferencesExtractor extractor(global_prop, this); | 427 ReferencesExtractor extractor(global_prop, this); |
390 obj->Iterate(&extractor); | 428 obj->Iterate(&extractor); |
391 } | 429 } |
392 } | 430 } |
393 | 431 |
394 | 432 |
395 void RetainerHeapProfile::DebugPrintStats( | 433 void RetainerHeapProfile::DebugPrintStats( |
396 RetainerHeapProfile::Printer* printer) { | 434 RetainerHeapProfile::Printer* printer) { |
397 coarser_.Process(&retainers_tree_); | 435 coarser_.Process(&retainers_tree_); |
398 ASSERT(current_printer_ == NULL); | 436 ASSERT(current_printer_ == NULL); |
399 current_printer_ = printer; | 437 current_printer_ = printer; |
400 retainers_tree_.ForEach(this); | 438 retainers_tree_.ForEach(this); |
401 current_printer_ = NULL; | 439 current_printer_ = NULL; |
402 } | 440 } |
403 | 441 |
404 | 442 |
405 void RetainerHeapProfile::PrintStats() { | 443 void RetainerHeapProfile::PrintStats() { |
406 RetainersPrinter printer; | 444 RetainersPrinter printer; |
407 DebugPrintStats(&printer); | 445 DebugPrintStats(&printer); |
408 } | 446 } |
409 | 447 |
410 | 448 |
411 void RetainerHeapProfile::Call( | 449 void RetainerHeapProfile::Call(const JSObjectsCluster& cluster, |
412 const JSObjectsCluster& cluster, | 450 JSObjectsClusterTree* tree) { |
413 JSObjectsClusterTree* tree) { | 451 // First level of retainer graph. |
414 ASSERT(current_printer_ != NULL); | 452 if (coarser_.HasAnEquivalent(cluster)) return; |
415 if (tree != NULL) { | 453 ASSERT(current_stream_ == NULL); |
416 // First level of retainer graph. | 454 HeapStringAllocator allocator; |
417 if (coarser_.HasAnEquivalent(cluster)) return; | 455 StringStream stream(&allocator); |
418 ASSERT(current_stream_ == NULL); | 456 current_stream_ = &stream; |
419 HeapStringAllocator allocator; | 457 ASSERT(coarse_cluster_tree_ == NULL); |
420 StringStream stream(&allocator); | 458 coarse_cluster_tree_ = new JSObjectsClusterTree(); |
421 current_stream_ = &stream; | 459 tree->ForEach(this); |
422 cluster.Print(current_stream_); | 460 // Print aggregated counts and sizes. |
423 ASSERT(coarse_cluster_tree_ == NULL); | 461 RetainerTreePrinter printer(current_stream_); |
424 coarse_cluster_tree_ = new JSObjectsClusterTree(); | 462 coarse_cluster_tree_->ForEach(&printer); |
425 retainers_printed_ = 0; | 463 coarse_cluster_tree_ = NULL; |
426 tree->ForEach(this); | 464 current_printer_->PrintRetainers(cluster, stream); |
427 coarse_cluster_tree_ = NULL; | 465 current_stream_ = NULL; |
428 current_printer_->PrintRetainers(stream); | 466 } |
429 current_stream_ = NULL; | 467 |
| 468 |
| 469 void RetainerHeapProfile::Call(const JSObjectsCluster& cluster, |
| 470 const NumberAndSizeInfo& number_and_size) { |
| 471 ASSERT(coarse_cluster_tree_ != NULL); |
| 472 ASSERT(current_stream_ != NULL); |
| 473 JSObjectsCluster eq = coarser_.GetCoarseEquivalent(cluster); |
| 474 if (eq.is_null()) { |
| 475 RetainerTreePrinter::Print(current_stream_, cluster, number_and_size); |
430 } else { | 476 } else { |
431 // Second level of retainer graph. | 477 // Aggregate counts and sizes for equivalent clusters. |
432 ASSERT(coarse_cluster_tree_ != NULL); | 478 JSObjectsClusterTree::Locator loc; |
433 ASSERT(current_stream_ != NULL); | 479 coarse_cluster_tree_->Insert(eq, &loc); |
434 if (retainers_printed_ >= kMaxRetainersToPrint) { | 480 NumberAndSizeInfo eq_number_and_size = loc.value(); |
435 if (retainers_printed_ == kMaxRetainersToPrint) { | 481 eq_number_and_size.increment_number(number_and_size.number()); |
436 // TODO(mnaganov): Print the exact count. | 482 loc.set_value(eq_number_and_size); |
437 current_stream_->Add(",..."); | |
438 ++retainers_printed_; // avoid printing ellipsis next time. | |
439 } | |
440 return; | |
441 } | |
442 JSObjectsCluster eq = coarser_.GetCoarseEquivalent(cluster); | |
443 if (eq.is_null()) { | |
444 current_stream_->Put(','); | |
445 cluster.Print(current_stream_); | |
446 ++retainers_printed_; | |
447 } else { | |
448 JSObjectsClusterTree::Locator loc; | |
449 if (coarse_cluster_tree_->Insert(eq, &loc)) { | |
450 current_stream_->Put(','); | |
451 eq.Print(current_stream_); | |
452 ++retainers_printed_; | |
453 } | |
454 } | |
455 } | 483 } |
456 } | 484 } |
457 | 485 |
458 | 486 |
459 // | 487 // |
460 // HeapProfiler class implementation. | 488 // HeapProfiler class implementation. |
461 // | 489 // |
462 void HeapProfiler::CollectStats(HeapObject* obj, HistogramInfo* info) { | 490 void HeapProfiler::CollectStats(HeapObject* obj, HistogramInfo* info) { |
463 InstanceType type = obj->map()->instance_type(); | 491 InstanceType type = obj->map()->instance_type(); |
464 ASSERT(0 <= type && type <= LAST_TYPE); | 492 ASSERT(0 <= type && type <= LAST_TYPE); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
510 js_retainer_profile.PrintStats(); | 538 js_retainer_profile.PrintStats(); |
511 | 539 |
512 LOG(HeapSampleEndEvent("Heap", "allocated")); | 540 LOG(HeapSampleEndEvent("Heap", "allocated")); |
513 } | 541 } |
514 | 542 |
515 | 543 |
516 #endif // ENABLE_LOGGING_AND_PROFILING | 544 #endif // ENABLE_LOGGING_AND_PROFILING |
517 | 545 |
518 | 546 |
519 } } // namespace v8::internal | 547 } } // namespace v8::internal |
OLD | NEW |