Index: src/heap.cc |
diff --git a/src/heap.cc b/src/heap.cc |
index 233f02f8a3daebb51467811fa4eb1823f4bc56a1..feb01202b64b4886d163d344bf38e175b1243198 100644 |
--- a/src/heap.cc |
+++ b/src/heap.cc |
@@ -3411,6 +3411,100 @@ void HeapIterator::reset() { |
} |
+#ifdef ENABLE_LOGGING_AND_PROFILING |
+namespace { |
+ |
+// JSConstructorProfile is responsible for gathering and logging |
+// "constructor profile" of JS object allocated on heap. |
+// It is run during garbage collection cycle, thus it doesn't need |
+// to use handles. |
+class JSConstructorProfile BASE_EMBEDDED { |
+ public: |
+ JSConstructorProfile() : zscope_(DELETE_ON_EXIT) {} |
+ void CollectStats(JSObject* obj); |
+ void PrintStats(); |
+ // Used by ZoneSplayTree::ForEach. |
+ void Call(String* name, const NumberAndSizeInfo& number_and_size); |
+ private: |
+ struct TreeConfig { |
+ typedef String* Key; |
+ typedef NumberAndSizeInfo Value; |
+ static const Key kNoKey; |
+ static const Value kNoValue; |
+ // Strings are unique, so it is sufficient to compare their pointers. |
+ static int Compare(const Key& a, const Key& b) { |
+ return a == b ? 0 : (a < b ? -1 : 1); |
+ } |
+ }; |
+ |
+ typedef ZoneSplayTree<TreeConfig> JSObjectsInfoTree; |
+ static int CalculateJSObjectNetworkSize(JSObject* obj); |
+ |
+ ZoneScope zscope_; |
+ JSObjectsInfoTree js_objects_info_tree_; |
+}; |
+ |
+const JSConstructorProfile::TreeConfig::Key |
+ JSConstructorProfile::TreeConfig::kNoKey = NULL; |
+const JSConstructorProfile::TreeConfig::Value |
+ JSConstructorProfile::TreeConfig::kNoValue; |
+ |
+ |
+int JSConstructorProfile::CalculateJSObjectNetworkSize(JSObject* obj) { |
+ int size = obj->Size(); |
+ // If 'properties' and 'elements' are non-empty (thus, non-shared), |
+ // take their size into account. |
+ if (FixedArray::cast(obj->properties())->length() != 0) { |
+ size += obj->properties()->Size(); |
+ } |
+ if (FixedArray::cast(obj->elements())->length() != 0) { |
+ size += obj->elements()->Size(); |
+ } |
+ return size; |
+} |
+ |
+ |
+void JSConstructorProfile::Call(String* name, |
+ const NumberAndSizeInfo& number_and_size) { |
+ SmartPointer<char> s_name; |
+ if (name != NULL) { |
+ s_name = name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); |
+ } |
+ LOG(HeapSampleJSConstructorEvent(*s_name, |
+ number_and_size.number(), |
+ number_and_size.bytes())); |
+} |
+ |
+ |
+void JSConstructorProfile::CollectStats(JSObject* obj) { |
+ String* constructor_func = NULL; |
+ if (obj->map()->constructor()->IsJSFunction()) { |
+ JSFunction* constructor = JSFunction::cast(obj->map()->constructor()); |
+ SharedFunctionInfo* sfi = constructor->shared(); |
+ String* name = String::cast(sfi->name()); |
+ constructor_func = name->length() > 0 ? name : sfi->inferred_name(); |
+ } else if (obj->IsJSFunction()) { |
+ constructor_func = Heap::function_class_symbol(); |
+ } |
+ JSObjectsInfoTree::Locator loc; |
+ if (!js_objects_info_tree_.Find(constructor_func, &loc)) { |
+ js_objects_info_tree_.Insert(constructor_func, &loc); |
+ } |
+ NumberAndSizeInfo number_and_size = loc.value(); |
+ number_and_size.increment_number(1); |
+ number_and_size.increment_bytes(CalculateJSObjectNetworkSize(obj)); |
+ loc.set_value(number_and_size); |
+} |
+ |
+ |
+void JSConstructorProfile::PrintStats() { |
+ js_objects_info_tree_.ForEach(this); |
+} |
+ |
+} // namespace |
+#endif |
+ |
+ |
// |
// HeapProfiler class implementation. |
// |
@@ -3435,9 +3529,14 @@ void HeapProfiler::WriteSample() { |
INSTANCE_TYPE_LIST(DEF_TYPE_NAME) |
#undef DEF_TYPE_NAME |
+ JSConstructorProfile js_cons_profile; |
HeapIterator iterator; |
while (iterator.has_next()) { |
- CollectStats(iterator.next(), info); |
+ HeapObject* obj = iterator.next(); |
+ CollectStats(obj, info); |
+ if (obj->IsJSObject()) { |
+ js_cons_profile.CollectStats(JSObject::cast(obj)); |
+ } |
} |
// Lump all the string types together. |
@@ -3459,6 +3558,8 @@ void HeapProfiler::WriteSample() { |
} |
} |
+ js_cons_profile.PrintStats(); |
+ |
LOG(HeapSampleEndEvent("Heap", "allocated")); |
} |