123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587 |
- #include "il2cpp-config.h"
- #include "gc/GarbageCollector.h"
- #include <utils/dynamic_array.h>
- #include "vm/Array.h"
- #include "vm/Class.h"
- #include "vm/ClassInlines.h"
- #include "vm/Field.h"
- #include "vm/Liveness.h"
- #include "vm/Type.h"
- #include "il2cpp-tabledefs.h"
- #include "il2cpp-class-internals.h"
- #include "il2cpp-object-internals.h"
- #define MARK_OBJ(obj) \
- do { \
- (obj)->klass = (Il2CppClass*)(((size_t)(obj)->klass) | (size_t)1); \
- } while (0)
- #define CLEAR_OBJ(obj) \
- do { \
- (obj)->klass = (Il2CppClass*)(((size_t)(obj)->klass) & ~(size_t)1); \
- } while (0)
- #define IS_MARKED(obj) \
- (((size_t)(obj)->klass) & (size_t)1)
- #define GET_CLASS(obj) \
- ((Il2CppClass*)(((size_t)(obj)->klass) & ~(size_t)1))
- namespace il2cpp
- {
- namespace vm
- {
- /* number of sub elements of an array to process before recursing
- * we take a depth first approach to use stack space rather than re-allocating
- * processing array which requires restarting world to ensure allocator lock is not held
- */
- const int kArrayElementsPerChunk = 256;
- /* how far we recurse processing array elements before we stop. Prevents stack overflow */
- const int kMaxTraverseRecursionDepth = 128;
- struct CustomGrowableBlockArray;
- struct LivenessState
- {
- LivenessState(Il2CppClass* filter, uint32_t maxCount, Liveness::register_object_callback callback, void*callback_userdata, Liveness::ReallocateArrayCallback reallocateArray);
- ~LivenessState();
- void Finalize();
- void Reset();
- void TraverseObjects();
- void FilterObjects();
- static void TraverseGenericObject(Il2CppObject* object, LivenessState* state);
- static void TraverseObject(Il2CppObject* object, LivenessState* state);
- static void TraverseGCDescriptor(Il2CppObject* object, LivenessState* state);
- static bool TraverseObjectInternal(Il2CppObject* object, bool isStruct, Il2CppClass* klass, LivenessState* state);
- static void TraverseArray(Il2CppArray* array, LivenessState* state);
- static bool AddProcessObject(Il2CppObject* object, LivenessState* state);
- static bool ShouldProcessValue(Il2CppObject* val, Il2CppClass* filter);
- static bool FieldCanContainReferences(FieldInfo* field);
- static bool ShouldTraverseObjects(size_t index, int32_t recursion_depth)
- {
- // Add kArrayElementsPerChunk objects at a time and then traverse
- return ((index + 1) & (kArrayElementsPerChunk - 1)) == 0 && recursion_depth < kMaxTraverseRecursionDepth;
- }
- CustomGrowableBlockArray* all_objects;
- Il2CppClass* filter;
- CustomGrowableBlockArray* process_array;
- void* callback_userdata;
- Liveness::register_object_callback filter_callback;
- Liveness::ReallocateArrayCallback reallocateArray;
- int32_t traverse_depth; // track recursion. Prevent stack overflow by limiting recurion
- };
- #define kBlockSize (8 * 1024)
- #define kArrayElementsPerBlock ((kBlockSize - 3 *sizeof (void*)) / sizeof (void*))
- struct CustomArrayBlock
- {
- Il2CppObject** next_item;
- CustomArrayBlock *prev_block;
- CustomArrayBlock *next_block;
- Il2CppObject* p_data[kArrayElementsPerBlock];
- };
- struct CustomBlockArrayIterator;
- struct CustomGrowableBlockArray
- {
- CustomArrayBlock *first_block;
- CustomArrayBlock *current_block;
- CustomBlockArrayIterator *iterator;
- CustomGrowableBlockArray(LivenessState *state);
- bool IsEmpty();
- void PushBack(Il2CppObject* value, LivenessState *state);
- Il2CppObject* PopBack();
- void ResetIterator();
- Il2CppObject* Next();
- void Clear();
- void Destroy(LivenessState *state);
- };
- struct CustomBlockArrayIterator
- {
- CustomGrowableBlockArray *array;
- CustomArrayBlock *current_block;
- Il2CppObject** current_position;
- };
- CustomGrowableBlockArray::CustomGrowableBlockArray(LivenessState *state)
- {
- current_block = (CustomArrayBlock*)state->reallocateArray(NULL, kBlockSize, state->callback_userdata);
- current_block->prev_block = NULL;
- current_block->next_block = NULL;
- current_block->next_item = current_block->p_data;
- first_block = current_block;
- iterator = new CustomBlockArrayIterator();
- iterator->array = this;
- iterator->current_block = first_block;
- iterator->current_position = first_block->p_data;
- }
- bool CustomGrowableBlockArray::IsEmpty()
- {
- return first_block->next_item == first_block->p_data;
- }
- void CustomGrowableBlockArray::PushBack(Il2CppObject* value, LivenessState *state)
- {
- if (current_block->next_item == current_block->p_data + kArrayElementsPerBlock)
- {
- CustomArrayBlock* new_block = current_block->next_block;
- if (current_block->next_block == NULL)
- {
- new_block = (CustomArrayBlock*)state->reallocateArray(NULL, kBlockSize, state->callback_userdata);
- new_block->next_block = NULL;
- new_block->prev_block = current_block;
- new_block->next_item = new_block->p_data;
- current_block->next_block = new_block;
- }
- current_block = new_block;
- }
- *current_block->next_item++ = value;
- }
- Il2CppObject* CustomGrowableBlockArray::PopBack()
- {
- if (current_block->next_item == current_block->p_data)
- {
- if (current_block->prev_block == NULL)
- return NULL;
- current_block = current_block->prev_block;
- current_block->next_item = current_block->p_data + kArrayElementsPerBlock;
- }
- return *--current_block->next_item;
- }
- void CustomGrowableBlockArray::ResetIterator()
- {
- iterator->current_block = first_block;
- iterator->current_position = first_block->p_data;
- }
- Il2CppObject* CustomGrowableBlockArray::Next()
- {
- if (iterator->current_position != iterator->current_block->next_item)
- return *iterator->current_position++;
- if (iterator->current_block->next_block == NULL)
- return NULL;
- iterator->current_block = iterator->current_block->next_block;
- iterator->current_position = iterator->current_block->p_data;
- if (iterator->current_position == iterator->current_block->next_item)
- return NULL;
- return *iterator->current_position++;
- }
- void CustomGrowableBlockArray::Clear()
- {
- CustomArrayBlock *block = first_block;
- while (block != NULL)
- {
- block->next_item = block->p_data;
- block = block->next_block;
- }
- }
- void CustomGrowableBlockArray::Destroy(LivenessState *state)
- {
- CustomArrayBlock *block = first_block;
- while (block != NULL)
- {
- CustomArrayBlock *data_block = block;
- block = block->next_block;
- state->reallocateArray(data_block, 0, state->callback_userdata);
- }
- delete iterator;
- delete this;
- }
- LivenessState::LivenessState(Il2CppClass* filter, uint32_t maxCount, Liveness::register_object_callback callback, void*callback_userdata, Liveness::ReallocateArrayCallback reallocateArray) :
- all_objects(NULL),
- filter(NULL),
- process_array(NULL),
- callback_userdata(NULL),
- filter_callback(NULL),
- reallocateArray(reallocateArray),
- traverse_depth(0)
- {
- // construct liveness_state;
- // allocate memory for the following structs
- // all_objects: contains a list of all referenced objects to be able to clean the vtable bits after the traversal
- // process_array. array that contains the objcets that should be processed. this should run depth first to reduce memory usage
- // if all_objects run out of space, run through list, add objects that match the filter, clear bit in vtable and then clear the array.
- this->filter = filter;
- this->callback_userdata = callback_userdata;
- this->filter_callback = callback;
- all_objects = new CustomGrowableBlockArray(this);
- process_array = new CustomGrowableBlockArray(this);
- }
- LivenessState::~LivenessState()
- {
- all_objects->Destroy(this);
- process_array->Destroy(this);
- }
- void LivenessState::Finalize()
- {
- all_objects->ResetIterator();
- Il2CppObject* object = all_objects->Next();
- while (object != NULL)
- {
- CLEAR_OBJ(object);
- object = all_objects->Next();
- }
- }
- void LivenessState::Reset()
- {
- process_array->Clear();
- }
- void LivenessState::TraverseObjects()
- {
- Il2CppObject* object = NULL;
- traverse_depth++;
- while (!process_array->IsEmpty())
- {
- object = process_array->PopBack();
- TraverseGenericObject(object, this);
- }
- traverse_depth--;
- }
- void LivenessState::FilterObjects()
- {
- Il2CppObject* filtered_objects[64];
- int32_t num_objects = 0;
- Il2CppObject* value = all_objects->Next();
- while (value)
- {
- Il2CppObject* object = value;
- if (ShouldProcessValue(object, filter))
- filtered_objects[num_objects++] = object;
- if (num_objects == 64)
- {
- filter_callback(filtered_objects, 64, callback_userdata);
- num_objects = 0;
- }
- value = all_objects->Next();
- }
- if (num_objects != 0)
- filter_callback(filtered_objects, num_objects, callback_userdata);
- }
- void LivenessState::TraverseGenericObject(Il2CppObject* object, LivenessState* state)
- {
- IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(LivenessState::TraverseGenericObject, "Use GC bitmap when we have one");
- #if IL2CPP_HAS_GC_DESCRIPTORS
- size_t gc_desc = (size_t)(GET_CLASS(object)->gc_desc);
- if (gc_desc & (size_t)1)
- TraverseGCDescriptor(object, state);
- else
- #endif
- if (GET_CLASS(object)->rank)
- TraverseArray((Il2CppArray*)object, state);
- else
- TraverseObject(object, state);
- }
- void LivenessState::TraverseObject(Il2CppObject* object, LivenessState* state)
- {
- TraverseObjectInternal(object, false, GET_CLASS(object), state);
- }
- void LivenessState::TraverseGCDescriptor(Il2CppObject* object, LivenessState* state)
- {
- #define WORDSIZE ((int)sizeof(size_t)*8)
- int i = 0;
- size_t mask = (size_t)(GET_CLASS(object)->gc_desc);
- IL2CPP_ASSERT(mask & (size_t)1);
- for (i = 0; i < WORDSIZE - 2; i++)
- {
- size_t offset = ((size_t)1 << (WORDSIZE - 1 - i));
- if (mask & offset)
- {
- Il2CppObject* val = *(Il2CppObject**)(((char*)object) + i * sizeof(void*));
- AddProcessObject(val, state);
- }
- }
- }
- bool LivenessState::TraverseObjectInternal(Il2CppObject* object, bool isStruct, Il2CppClass* klass, LivenessState* state)
- {
- FieldInfo *field;
- Il2CppClass *p;
- bool added_objects = false;
- IL2CPP_ASSERT(object);
- if (!klass->size_inited)
- {
- IL2CPP_ASSERT(isStruct);
- return false;
- }
- // subtract the added offset for the vtable. This is added to the offset even though it is a struct
- if (isStruct)
- object--;
- for (p = klass; p != NULL; p = p->parent)
- {
- void* iter = NULL;
- while ((field = Class::GetFields(p, &iter)))
- {
- if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
- continue;
- if (!FieldCanContainReferences(field))
- continue;
- if (Type::IsStruct(field->type))
- {
- char* offseted = (char*)object;
- offseted += field->offset;
- if (Type::IsGenericInstance(field->type))
- {
- IL2CPP_ASSERT(field->type->data.generic_class->cached_class);
- added_objects |= TraverseObjectInternal((Il2CppObject*)offseted, true, field->type->data.generic_class->cached_class, state);
- }
- else
- added_objects |= TraverseObjectInternal((Il2CppObject*)offseted, true, Type::GetClass(field->type), state);
- continue;
- }
- if (field->offset == THREAD_STATIC_FIELD_OFFSET)
- {
- IL2CPP_ASSERT(0);
- }
- else
- {
- Il2CppObject* val = NULL;
- Field::GetValue(object, field, &val);
- added_objects |= AddProcessObject(val, state);
- }
- }
- }
- return added_objects;
- }
- void LivenessState::TraverseArray(Il2CppArray* array, LivenessState* state)
- {
- size_t i = 0;
- bool has_references;
- Il2CppObject* object = (Il2CppObject*)array;
- Il2CppClass* element_class;
- size_t elementClassSize;
- size_t array_length;
- IL2CPP_ASSERT(object);
- element_class = GET_CLASS(object)->element_class;
- has_references = !Class::IsValuetype(element_class);
- IL2CPP_ASSERT(element_class->size_inited != 0);
- FieldInfo* field;
- void* iter = NULL;
- while ((field = Class::GetFields(element_class, &iter)))
- {
- has_references |= FieldCanContainReferences(field);
- if (has_references)
- break;
- }
- if (!has_references)
- return;
- array_length = Array::GetLength(array);
- if (element_class->byval_arg.valuetype)
- {
- size_t items_processed = 0;
- elementClassSize = Class::GetArrayElementSize(element_class);
- for (i = 0; i < array_length; i++)
- {
- Il2CppObject* object = (Il2CppObject*)il2cpp_array_addr_with_size(array, (int32_t)elementClassSize, i);
- if (TraverseObjectInternal(object, 1, element_class, state))
- items_processed++;
- // Add 64 objects at a time and then traverse
- if (ShouldTraverseObjects(items_processed, state->traverse_depth))
- state->TraverseObjects();
- }
- }
- else
- {
- size_t items_processed = 0;
- for (i = 0; i < array_length; i++)
- {
- Il2CppObject* val = il2cpp_array_get(array, Il2CppObject*, i);
- if (AddProcessObject(val, state))
- items_processed++;
- // Add 64 objects at a time and then traverse
- if (ShouldTraverseObjects(items_processed, state->traverse_depth))
- state->TraverseObjects();
- }
- }
- }
- bool LivenessState::AddProcessObject(Il2CppObject* object, LivenessState* state)
- {
- if (!object || IS_MARKED(object))
- return false;
- bool has_references = GET_CLASS(object)->has_references;
- if (has_references || ShouldProcessValue(object, state->filter))
- {
- state->all_objects->PushBack(object, state);
- MARK_OBJ(object);
- }
- // Check if klass has further references - if not skip adding
- if (has_references)
- {
- state->process_array->PushBack(object, state);
- return true;
- }
- return false;
- }
- bool LivenessState::ShouldProcessValue(Il2CppObject* val, Il2CppClass* filter)
- {
- Il2CppClass* val_class = GET_CLASS(val);
- if (filter && !ClassInlines::HasParentUnsafe(val_class, filter))
- return false;
- return true;
- }
- bool LivenessState::FieldCanContainReferences(FieldInfo* field)
- {
- if (Type::IsStruct(field->type))
- return true;
- if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL)
- return false;
- if (field->type->type == IL2CPP_TYPE_STRING)
- return false;
- return Type::IsReference(field->type);
- }
- void* Liveness::AllocateStruct(Il2CppClass* filter, int max_object_count, register_object_callback callback, void* userdata, ReallocateArrayCallback reallocateArray)
- {
- // ensure filter is initialized so we can do fast (and lock free) check HasParentUnsafe
- Class::SetupTypeHierarchy(filter);
- LivenessState* state = new LivenessState(filter, max_object_count, callback, userdata, reallocateArray);
- // no allocations can happen beyond this point
- return state;
- }
- void Liveness::FreeStruct(void* state)
- {
- LivenessState* lstate = (LivenessState*)state;
- delete lstate;
- }
- void Liveness::Finalize(void* state)
- {
- LivenessState* lstate = (LivenessState*)state;
- lstate->Finalize();
- }
- void Liveness::FromRoot(Il2CppObject* root, void* state)
- {
- LivenessState* liveness_state = (LivenessState*)state;
- liveness_state->Reset();
- liveness_state->process_array->PushBack(root, liveness_state);
- liveness_state->TraverseObjects();
- //Filter objects and call callback to register found objects
- liveness_state->FilterObjects();
- }
- void Liveness::FromStatics(void* state)
- {
- LivenessState* liveness_state = (LivenessState*)state;
- const il2cpp::utils::dynamic_array<Il2CppClass*>& classesWithStatics = Class::GetStaticFieldData();
- liveness_state->Reset();
- for (il2cpp::utils::dynamic_array<Il2CppClass*>::const_iterator iter = classesWithStatics.begin();
- iter != classesWithStatics.end();
- iter++)
- {
- Il2CppClass* klass = *iter;
- FieldInfo *field;
- if (!klass)
- continue;
- if (klass->image == il2cpp_defaults.corlib)
- continue;
- if (klass->size_inited == 0)
- continue;
- void* fieldIter = NULL;
- while ((field = Class::GetFields(klass, &fieldIter)))
- {
- if (!vm::Field::IsNormalStatic(field))
- continue;
- if (!LivenessState::FieldCanContainReferences(field))
- continue;
- if (Type::IsStruct(field->type))
- {
- char* offseted = (char*)klass->static_fields;
- offseted += field->offset;
- if (Type::IsGenericInstance(field->type))
- {
- IL2CPP_ASSERT(field->type->data.generic_class->cached_class);
- LivenessState::TraverseObjectInternal((Il2CppObject*)offseted, true, field->type->data.generic_class->cached_class, liveness_state);
- }
- else
- {
- LivenessState::TraverseObjectInternal((Il2CppObject*)offseted, true, Type::GetClass(field->type), liveness_state);
- }
- }
- else
- {
- Il2CppObject* val = NULL;
- Field::StaticGetValue(field, &val);
- if (val)
- {
- LivenessState::AddProcessObject(val, liveness_state);
- }
- }
- }
- }
- liveness_state->TraverseObjects();
- //Filter objects and call callback to register found objects
- liveness_state->FilterObjects();
- }
- } /* namespace vm */
- } /* namespace il2cpp */
|