| 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 |