Commit 021f02b5 authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

[object-stats] Add virtual types

- JSObject: Record elements and properties
- JSCollecton: Record table
- Record global caches

Bug: v8:7266
Change-Id: I16b2eb511bed3dc0fb6f7af0e7037c6d42f03885
Reviewed-on: https://chromium-review.googlesource.com/878326
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50766}
parent 9f7fb728
......@@ -208,6 +208,22 @@ class ObjectStatsCollectorImpl {
void CollectStatistics(HeapObject* obj, Phase phase);
private:
enum CowMode {
kCheckCow,
kIgnoreCow,
};
void RecordVirtualObjectStats(HeapObject* parent, HeapObject* obj,
ObjectStats::VirtualInstanceType type,
size_t size, size_t over_allocated);
// Gets size from |ob| and assumes no over allocating.
void RecordSimpleVirtualObjectStats(HeapObject* parent, HeapObject* obj,
ObjectStats::VirtualInstanceType type);
// For HashTable it is possible to compute over allocated memory.
void RecordHashTableVirtualObjectStats(HeapObject* parent,
FixedArray* hash_table,
ObjectStats::VirtualInstanceType type);
bool SameLiveness(HeapObject* obj1, HeapObject* obj2);
bool CanRecordFixedArray(FixedArrayBase* array);
bool IsCowArray(FixedArrayBase* array);
......@@ -215,20 +231,19 @@ class ObjectStatsCollectorImpl {
// Blacklist for objects that should not be recorded using
// VirtualObjectStats and RecordSimpleVirtualObjectStats. For recording those
// objects dispatch to the low level ObjectStats::RecordObjectStats manually.
bool ShouldRecordObject(HeapObject* object);
bool ShouldRecordObject(HeapObject* object, CowMode check_cow_array);
void RecordObjectStats(HeapObject* obj, InstanceType type, size_t size);
void RecordVirtualObjectStats(HeapObject* parent, HeapObject* obj,
ObjectStats::VirtualInstanceType type,
size_t size, size_t over_allocated);
// Gets size from |ob| and assumes no over allocating.
void RecordSimpleVirtualObjectStats(HeapObject* parent, HeapObject* obj,
ObjectStats::VirtualInstanceType type);
// Details.
void RecordVirtualAllocationSiteDetails(AllocationSite* site);
void RecordVirtualBytecodeArrayDetails(BytecodeArray* bytecode);
void RecordVirtualCodeDetails(Code* code);
void RecordVirtualFeedbackVectorDetails(FeedbackVector* vector);
void RecordVirtualFixedArrayDetails(FixedArray* array);
void RecordVirtualJSGlobalObjectDetails(JSGlobalObject* object);
void RecordVirtualJSCollectionDetails(JSObject* object);
void RecordVirtualJSObjectDetails(JSObject* object);
void RecordVirtualMapDetails(Map* map);
Heap* heap_;
......@@ -244,15 +259,26 @@ ObjectStatsCollectorImpl::ObjectStatsCollectorImpl(Heap* heap,
marking_state_(
heap->mark_compact_collector()->non_atomic_marking_state()) {}
bool ObjectStatsCollectorImpl::ShouldRecordObject(HeapObject* obj) {
bool ObjectStatsCollectorImpl::ShouldRecordObject(HeapObject* obj,
CowMode check_cow_array) {
if (obj->IsFixedArray()) {
FixedArray* fixed_array = FixedArray::cast(obj);
return CanRecordFixedArray(fixed_array) && !IsCowArray(fixed_array);
bool cow_check = check_cow_array == kIgnoreCow || !IsCowArray(fixed_array);
return CanRecordFixedArray(fixed_array) && cow_check;
}
if (obj == heap_->empty_property_array()) return false;
return true;
}
void ObjectStatsCollectorImpl::RecordHashTableVirtualObjectStats(
HeapObject* parent, FixedArray* hash_table,
ObjectStats::VirtualInstanceType type) {
CHECK(hash_table->IsHashTable());
// TODO(mlippautz): Implement over allocation for hash tables.
RecordVirtualObjectStats(parent, hash_table, type, hash_table->Size(),
ObjectStats::kNoOverAllocation);
}
void ObjectStatsCollectorImpl::RecordSimpleVirtualObjectStats(
HeapObject* parent, HeapObject* obj,
ObjectStats::VirtualInstanceType type) {
......@@ -263,19 +289,12 @@ void ObjectStatsCollectorImpl::RecordSimpleVirtualObjectStats(
void ObjectStatsCollectorImpl::RecordVirtualObjectStats(
HeapObject* parent, HeapObject* obj, ObjectStats::VirtualInstanceType type,
size_t size, size_t over_allocated) {
if (!SameLiveness(parent, obj) || !ShouldRecordObject(obj)) return;
#if DEBUG
if (virtual_objects_.find(obj) != virtual_objects_.end()) {
std::stringstream description;
obj->Print(description);
V8_Fatal(__FILE__, __LINE__,
"Object with virtual instance type has been recorded before:\n%s",
description.str().c_str());
if (!SameLiveness(parent, obj) || !ShouldRecordObject(obj, kCheckCow)) return;
if (virtual_objects_.find(obj) == virtual_objects_.end()) {
virtual_objects_.insert(obj);
stats_->RecordVirtualObjectStats(type, size, over_allocated);
}
#endif
virtual_objects_.insert(obj);
stats_->RecordVirtualObjectStats(type, size, over_allocated);
}
void ObjectStatsCollectorImpl::RecordVirtualAllocationSiteDetails(
......@@ -283,9 +302,8 @@ void ObjectStatsCollectorImpl::RecordVirtualAllocationSiteDetails(
if (!site->PointsToLiteral()) return;
JSObject* boilerplate = site->boilerplate();
if (boilerplate->IsJSArray()) {
RecordVirtualObjectStats(
site, boilerplate, ObjectStats::JS_ARRAY_BOILERPLATE_TYPE,
boilerplate->Size(), ObjectStats::kNoOverAllocation);
RecordSimpleVirtualObjectStats(site, boilerplate,
ObjectStats::JS_ARRAY_BOILERPLATE_TYPE);
// Array boilerplates cannot have properties.
} else {
RecordVirtualObjectStats(
......@@ -295,26 +313,64 @@ void ObjectStatsCollectorImpl::RecordVirtualAllocationSiteDetails(
// We'll mis-classify the empty_property_array here. Given that there is a
// single instance, this is negligible.
PropertyArray* properties = boilerplate->property_array();
RecordVirtualObjectStats(
site, properties, ObjectStats::BOILERPLATE_PROPERTY_ARRAY_TYPE,
properties->Size(), ObjectStats::kNoOverAllocation);
RecordSimpleVirtualObjectStats(
site, properties, ObjectStats::BOILERPLATE_PROPERTY_ARRAY_TYPE);
} else {
NameDictionary* properties = boilerplate->property_dictionary();
RecordVirtualObjectStats(
site, properties, ObjectStats::BOILERPLATE_NAME_DICTIONARY_TYPE,
properties->Size(), ObjectStats::kNoOverAllocation);
RecordSimpleVirtualObjectStats(
site, properties, ObjectStats::BOILERPLATE_PROPERTY_DICTIONARY_TYPE);
}
}
FixedArrayBase* elements = boilerplate->elements();
// We skip COW elements since they are shared, and we are sure that if the
// boilerplate exists there must have been at least one instantiation.
if (!elements->IsCowArray()) {
RecordVirtualObjectStats(site, elements,
ObjectStats::BOILERPLATE_ELEMENTS_TYPE,
elements->Size(), ObjectStats::kNoOverAllocation);
RecordSimpleVirtualObjectStats(site, elements,
ObjectStats::BOILERPLATE_ELEMENTS_TYPE);
}
void ObjectStatsCollectorImpl::RecordVirtualJSGlobalObjectDetails(
JSGlobalObject* object) {
// Properties.
GlobalDictionary* properties = object->global_dictionary();
RecordHashTableVirtualObjectStats(object, properties,
ObjectStats::GLOBAL_PROPERTIES_TYPE);
// Elements.
FixedArrayBase* elements = object->elements();
RecordSimpleVirtualObjectStats(object, elements,
ObjectStats::GLOBAL_ELEMENTS_TYPE);
}
void ObjectStatsCollectorImpl::RecordVirtualJSCollectionDetails(
JSObject* object) {
if (object->IsJSMap()) {
RecordSimpleVirtualObjectStats(
object, FixedArray::cast(JSMap::cast(object)->table()),
ObjectStats::JS_COLLETION_TABLE_TYPE);
}
if (object->IsJSSet()) {
RecordSimpleVirtualObjectStats(
object, FixedArray::cast(JSSet::cast(object)->table()),
ObjectStats::JS_COLLETION_TABLE_TYPE);
}
}
void ObjectStatsCollectorImpl::RecordVirtualJSObjectDetails(JSObject* object) {
// JSGlobalObject is recorded separately.
if (object->IsJSGlobalObject()) return;
// Properties.
if (object->HasFastProperties()) {
PropertyArray* properties = object->property_array();
RecordSimpleVirtualObjectStats(object, properties,
ObjectStats::PROPERTY_ARRAY_TYPE);
} else {
NameDictionary* properties = object->property_dictionary();
RecordHashTableVirtualObjectStats(object, properties,
ObjectStats::PROPERTY_DICTIONARY_TYPE);
}
// Elements.
FixedArrayBase* elements = object->elements();
RecordSimpleVirtualObjectStats(object, elements, ObjectStats::ELEMENTS_TYPE);
}
void ObjectStatsCollectorImpl::RecordVirtualFeedbackVectorDetails(
FeedbackVector* vector) {
// Except for allocation
......@@ -323,9 +379,24 @@ void ObjectStatsCollectorImpl::RecordVirtualFeedbackVectorDetails(
if (!raw_object->IsHeapObject()) continue;
HeapObject* object = HeapObject::cast(raw_object);
if (object->IsCell() || object->IsFixedArray()) {
RecordVirtualObjectStats(vector, object,
ObjectStats::FEEDBACK_VECTOR_ENTRY_TYPE,
object->Size(), ObjectStats::kNoOverAllocation);
RecordSimpleVirtualObjectStats(vector, object,
ObjectStats::FEEDBACK_VECTOR_ENTRY_TYPE);
}
}
}
void ObjectStatsCollectorImpl::RecordVirtualFixedArrayDetails(
FixedArray* array) {
if (IsCowArray(array)) {
// Manually check and dispatch to lower level recording due to COW array
// check. No need for SameLiveness as we call it on a single object.
if (!ShouldRecordObject(array, kIgnoreCow)) return;
if (virtual_objects_.find(array) == virtual_objects_.end()) {
virtual_objects_.insert(array);
stats_->RecordVirtualObjectStats(ObjectStats::COW_ARRAY_TYPE,
array->Size(),
ObjectStats::kNoOverAllocation);
}
}
}
......@@ -342,6 +413,16 @@ void ObjectStatsCollectorImpl::CollectStatistics(HeapObject* obj, Phase phase) {
RecordVirtualBytecodeArrayDetails(BytecodeArray::cast(obj));
} else if (obj->IsCode()) {
RecordVirtualCodeDetails(Code::cast(obj));
} else if (obj->IsJSGlobalObject()) {
RecordVirtualJSGlobalObjectDetails(JSGlobalObject::cast(obj));
} else if (obj->IsJSObject()) {
// This phase needs to come after RecordVirtualAllocationSiteDetails
// to properly split among boilerplates.
RecordVirtualJSObjectDetails(JSObject::cast(obj));
} else if (obj->IsJSCollection()) {
RecordVirtualJSCollectionDetails(JSObject::cast(obj));
} else if (obj->IsFixedArray()) {
RecordVirtualFixedArrayDetails(FixedArray::cast(obj));
}
break;
case kPhase2:
......@@ -359,7 +440,7 @@ void ObjectStatsCollectorImpl::CollectGlobalStatistics() {
list = site->weak_next();
}
// Global FixedArrays.
// FixedArray.
RecordSimpleVirtualObjectStats(
nullptr, heap_->weak_new_space_object_to_code_list(),
ObjectStats::WEAK_NEW_SPACE_OBJECT_TO_CODE_TYPE);
......@@ -377,7 +458,7 @@ void ObjectStatsCollectorImpl::CollectGlobalStatistics() {
RecordSimpleVirtualObjectStats(nullptr, heap_->retained_maps(),
ObjectStats::RETAINED_MAPS_TYPE);
// Global weak FixedArrays.
// WeakFixedArray.
RecordSimpleVirtualObjectStats(
nullptr, WeakFixedArray::cast(heap_->noscript_shared_function_infos()),
ObjectStats::NOSCRIPT_SHARED_FUNCTION_INFOS_TYPE);
......@@ -385,12 +466,15 @@ void ObjectStatsCollectorImpl::CollectGlobalStatistics() {
WeakFixedArray::cast(heap_->script_list()),
ObjectStats::SCRIPT_LIST_TYPE);
// Global hash tables.
// TODO(mlippautz):
// - heap_->string_table(): STRING_TABLE_TYPE
// - heap_->weak_object_to_code_table(): OBJECT_TO_CODE_TYPE
// - heap_->code_stubs(): CODE_STUBS_TABLE_TYPE
// - heap_->empty_property_dictionary(): EMPTY_PROPERTIES_DICTIONARY_TYPE
// HashTable.
RecordHashTableVirtualObjectStats(nullptr, heap_->string_table(),
ObjectStats::STRING_TABLE_TYPE);
RecordHashTableVirtualObjectStats(nullptr, heap_->code_stubs(),
ObjectStats::CODE_STUBS_TABLE_TYPE);
// WeakHashTable.
RecordHashTableVirtualObjectStats(nullptr, heap_->weak_object_to_code_table(),
ObjectStats::OBJECT_TO_CODE_TYPE);
}
void ObjectStatsCollectorImpl::RecordObjectStats(HeapObject* obj,
......@@ -402,8 +486,7 @@ void ObjectStatsCollectorImpl::RecordObjectStats(HeapObject* obj,
}
bool ObjectStatsCollectorImpl::CanRecordFixedArray(FixedArrayBase* array) {
return array->map()->instance_type() == FIXED_ARRAY_TYPE &&
array != heap_->empty_fixed_array() &&
return array != heap_->empty_fixed_array() &&
array != heap_->empty_sloppy_arguments_elements() &&
array != heap_->empty_slow_element_dictionary() &&
array != heap_->empty_property_dictionary();
......
......@@ -19,28 +19,38 @@
// tracing.
//
// Update LAST_VIRTUAL_TYPE below when changing this macro.
#define VIRTUAL_INSTANCE_TYPE_LIST(V) \
CODE_KIND_LIST(V) \
V(BOILERPLATE_ELEMENTS_TYPE) \
V(BOILERPLATE_NAME_DICTIONARY_TYPE) \
V(BOILERPLATE_PROPERTY_ARRAY_TYPE) \
V(BYTECODE_ARRAY_CONSTANT_POOL_TYPE) \
V(BYTECODE_ARRAY_HANDLER_TABLE_TYPE) \
V(DEPENDENT_CODE_TYPE) \
V(ENUM_CACHE_TYPE) \
V(ENUM_INDICES_CACHE_TYPE) \
V(FEEDBACK_VECTOR_ENTRY_TYPE) \
V(JS_ARRAY_BOILERPLATE_TYPE) \
V(JS_OBJECT_BOILERPLATE_TYPE) \
V(NOSCRIPT_SHARED_FUNCTION_INFOS_TYPE) \
V(NUMBER_STRING_CACHE_TYPE) \
V(PROTOTYPE_USERS_TYPE) \
V(REGEXP_MULTIPLE_CACHE_TYPE) \
V(RETAINED_MAPS_TYPE) \
V(SCRIPT_LIST_TYPE) \
V(SERIALIZED_OBJECTS_TYPE) \
V(SINGLE_CHARACTER_STRING_CACHE_TYPE) \
V(STRING_SPLIT_CACHE_TYPE) \
#define VIRTUAL_INSTANCE_TYPE_LIST(V) \
CODE_KIND_LIST(V) \
V(BOILERPLATE_ELEMENTS_TYPE) \
V(BOILERPLATE_PROPERTY_ARRAY_TYPE) \
V(BOILERPLATE_PROPERTY_DICTIONARY_TYPE) \
V(BYTECODE_ARRAY_CONSTANT_POOL_TYPE) \
V(BYTECODE_ARRAY_HANDLER_TABLE_TYPE) \
V(CODE_STUBS_TABLE_TYPE) \
V(COW_ARRAY_TYPE) \
V(DEPENDENT_CODE_TYPE) \
V(ELEMENTS_TYPE) \
V(ENUM_CACHE_TYPE) \
V(ENUM_INDICES_CACHE_TYPE) \
V(FEEDBACK_VECTOR_ENTRY_TYPE) \
V(GLOBAL_ELEMENTS_TYPE) \
V(GLOBAL_PROPERTIES_TYPE) \
V(JS_ARRAY_BOILERPLATE_TYPE) \
V(JS_COLLETION_TABLE_TYPE) \
V(JS_OBJECT_BOILERPLATE_TYPE) \
V(NOSCRIPT_SHARED_FUNCTION_INFOS_TYPE) \
V(NUMBER_STRING_CACHE_TYPE) \
V(OBJECT_TO_CODE_TYPE) \
V(PROPERTY_ARRAY_TYPE) \
V(PROPERTY_DICTIONARY_TYPE) \
V(PROTOTYPE_USERS_TYPE) \
V(REGEXP_MULTIPLE_CACHE_TYPE) \
V(RETAINED_MAPS_TYPE) \
V(SCRIPT_LIST_TYPE) \
V(SERIALIZED_OBJECTS_TYPE) \
V(SINGLE_CHARACTER_STRING_CACHE_TYPE) \
V(STRING_SPLIT_CACHE_TYPE) \
V(STRING_TABLE_TYPE) \
V(WEAK_NEW_SPACE_OBJECT_TO_CODE_TYPE)
namespace v8 {
......
......@@ -9,6 +9,7 @@ const CATEGORIES = new Map([
'CONS_ONE_BYTE_STRING_TYPE',
'CONS_STRING_TYPE',
'DESCRIPTOR_ARRAY_TYPE',
'ELEMENTS_TYPE',
'EXTERNAL_INTERNALIZED_STRING_TYPE',
'EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE',
'EXTERNAL_ONE_BYTE_STRING_TYPE',
......@@ -24,6 +25,8 @@ const CATEGORIES = new Map([
'FIXED_UINT32_ARRAY_TYPE',
'FIXED_UINT8_ARRAY_TYPE',
'FIXED_UINT8_CLAMPED_ARRAY_TYPE',
'GLOBAL_ELEMENTS_TYPE',
'GLOBAL_PROPERTIES_TYPE',
'HEAP_NUMBER_TYPE',
'INTERNALIZED_STRING_TYPE',
'JS_ARGUMENTS_TYPE',
......@@ -50,6 +53,7 @@ const CATEGORIES = new Map([
'JS_PROMISE_TYPE',
'JS_REGEXP_TYPE',
'JS_SET_TYPE',
'JS_SET_VALUE_ITERATOR_TYPE',
'JS_STRING_ITERATOR_TYPE',
'JS_TYPED_ARRAY_TYPE',
'JS_VALUE_TYPE',
......@@ -58,6 +62,7 @@ const CATEGORIES = new Map([
'ONE_BYTE_INTERNALIZED_STRING_TYPE',
'ONE_BYTE_STRING_TYPE',
'PROPERTY_ARRAY_TYPE',
'PROPERTY_DICTIONARY_TYPE',
'SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE',
'SHORT_EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE',
'SHORT_EXTERNAL_ONE_BYTE_STRING_TYPE',
......@@ -78,10 +83,11 @@ const CATEGORIES = new Map([
'ALLOCATION_MEMENTO_TYPE',
'ALLOCATION_SITE_TYPE',
'BOILERPLATE_ELEMENTS_TYPE',
'BOILERPLATE_NAME_DICTIONARY_TYPE',
'BOILERPLATE_PROPERTY_ARRAY_TYPE',
'BOILERPLATE_PROPERTY_DICTIONARY_TYPE',
'BYTE_ARRAY_TYPE',
'CELL_TYPE',
'CODE_STUBS_TABLE_TYPE',
'CONTEXT_EXTENSION_TYPE',
'ENUM_CACHE_TYPE',
'ENUM_INDICES_CACHE_TYPE',
......@@ -93,16 +99,26 @@ const CATEGORIES = new Map([
'JS_OBJECT_BOILERPLATE_TYPE',
'JS_SPECIAL_API_OBJECT_TYPE',
'MAP_TYPE',
'NUMBER_STRING_CACHE_TYPE',
'OBJECT_TEMPLATE_INFO_TYPE',
'OBJECT_TO_CODE_TYPE',
'ODDBALL_TYPE',
'PROMISE_REACTION_JOB_INFO_TYPE',
'PROMISE_RESOLVE_THENABLE_JOB_INFO_TYPE',
'PROPERTY_CELL_TYPE',
'PROTOTYPE_INFO_TYPE',
'PROTOTYPE_USERS_TYPE',
'REGEXP_MULTIPLE_CACHE_TYPE',
'RETAINED_MAPS_TYPE',
'SCRIPT_LIST_TYPE',
'SERIALIZED_OBJECTS_TYPE',
'SINGLE_CHARACTER_STRING_CACHE_TYPE',
'STACK_FRAME_INFO_TYPE',
'STRING_SPLIT_CACHE_TYPE',
'STRING_TABLE_TYPE',
'TRANSITION_ARRAY_TYPE',
'WEAK_CELL_TYPE'
'WEAK_CELL_TYPE',
'WEAK_NEW_SPACE_OBJECT_TO_CODE_TYPE',
])
],
[
......@@ -119,6 +135,7 @@ const CATEGORIES = new Map([
'FEEDBACK_VECTOR_ENTRY_TYPE',
'FEEDBACK_VECTOR_TYPE',
'LOAD_HANDLER_TYPE',
'NOSCRIPT_SHARED_FUNCTION_INFOS_TYPE',
'SCRIPT_TYPE',
'SHARED_FUNCTION_INFO_TYPE',
'STORE_HANDLER_TYPE',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment