Liveness.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. #include "il2cpp-config.h"
  2. #include "gc/GarbageCollector.h"
  3. #include <utils/dynamic_array.h>
  4. #include "vm/Array.h"
  5. #include "vm/Class.h"
  6. #include "vm/ClassInlines.h"
  7. #include "vm/Field.h"
  8. #include "vm/Liveness.h"
  9. #include "vm/Type.h"
  10. #include "il2cpp-tabledefs.h"
  11. #include "il2cpp-class-internals.h"
  12. #include "il2cpp-object-internals.h"
  13. #define MARK_OBJ(obj) \
  14. do { \
  15. (obj)->klass = (Il2CppClass*)(((size_t)(obj)->klass) | (size_t)1); \
  16. } while (0)
  17. #define CLEAR_OBJ(obj) \
  18. do { \
  19. (obj)->klass = (Il2CppClass*)(((size_t)(obj)->klass) & ~(size_t)1); \
  20. } while (0)
  21. #define IS_MARKED(obj) \
  22. (((size_t)(obj)->klass) & (size_t)1)
  23. #define GET_CLASS(obj) \
  24. ((Il2CppClass*)(((size_t)(obj)->klass) & ~(size_t)1))
  25. namespace il2cpp
  26. {
  27. namespace vm
  28. {
  29. /* number of sub elements of an array to process before recursing
  30. * we take a depth first approach to use stack space rather than re-allocating
  31. * processing array which requires restarting world to ensure allocator lock is not held
  32. */
  33. const int kArrayElementsPerChunk = 256;
  34. /* how far we recurse processing array elements before we stop. Prevents stack overflow */
  35. const int kMaxTraverseRecursionDepth = 128;
  36. struct CustomGrowableBlockArray;
  37. struct LivenessState
  38. {
  39. LivenessState(Il2CppClass* filter, uint32_t maxCount, Liveness::register_object_callback callback, void*callback_userdata, Liveness::ReallocateArrayCallback reallocateArray);
  40. ~LivenessState();
  41. void Finalize();
  42. void Reset();
  43. void TraverseObjects();
  44. void FilterObjects();
  45. static void TraverseGenericObject(Il2CppObject* object, LivenessState* state);
  46. static void TraverseObject(Il2CppObject* object, LivenessState* state);
  47. static void TraverseGCDescriptor(Il2CppObject* object, LivenessState* state);
  48. static bool TraverseObjectInternal(Il2CppObject* object, bool isStruct, Il2CppClass* klass, LivenessState* state);
  49. static void TraverseArray(Il2CppArray* array, LivenessState* state);
  50. static bool AddProcessObject(Il2CppObject* object, LivenessState* state);
  51. static bool ShouldProcessValue(Il2CppObject* val, Il2CppClass* filter);
  52. static bool FieldCanContainReferences(FieldInfo* field);
  53. static bool ShouldTraverseObjects(size_t index, int32_t recursion_depth)
  54. {
  55. // Add kArrayElementsPerChunk objects at a time and then traverse
  56. return ((index + 1) & (kArrayElementsPerChunk - 1)) == 0 && recursion_depth < kMaxTraverseRecursionDepth;
  57. }
  58. CustomGrowableBlockArray* all_objects;
  59. Il2CppClass* filter;
  60. CustomGrowableBlockArray* process_array;
  61. void* callback_userdata;
  62. Liveness::register_object_callback filter_callback;
  63. Liveness::ReallocateArrayCallback reallocateArray;
  64. int32_t traverse_depth; // track recursion. Prevent stack overflow by limiting recurion
  65. };
  66. #define kBlockSize (8 * 1024)
  67. #define kArrayElementsPerBlock ((kBlockSize - 3 *sizeof (void*)) / sizeof (void*))
  68. struct CustomArrayBlock
  69. {
  70. Il2CppObject** next_item;
  71. CustomArrayBlock *prev_block;
  72. CustomArrayBlock *next_block;
  73. Il2CppObject* p_data[kArrayElementsPerBlock];
  74. };
  75. struct CustomBlockArrayIterator;
  76. struct CustomGrowableBlockArray
  77. {
  78. CustomArrayBlock *first_block;
  79. CustomArrayBlock *current_block;
  80. CustomBlockArrayIterator *iterator;
  81. CustomGrowableBlockArray(LivenessState *state);
  82. bool IsEmpty();
  83. void PushBack(Il2CppObject* value, LivenessState *state);
  84. Il2CppObject* PopBack();
  85. void ResetIterator();
  86. Il2CppObject* Next();
  87. void Clear();
  88. void Destroy(LivenessState *state);
  89. };
  90. struct CustomBlockArrayIterator
  91. {
  92. CustomGrowableBlockArray *array;
  93. CustomArrayBlock *current_block;
  94. Il2CppObject** current_position;
  95. };
  96. CustomGrowableBlockArray::CustomGrowableBlockArray(LivenessState *state)
  97. {
  98. current_block = (CustomArrayBlock*)state->reallocateArray(NULL, kBlockSize, state->callback_userdata);
  99. current_block->prev_block = NULL;
  100. current_block->next_block = NULL;
  101. current_block->next_item = current_block->p_data;
  102. first_block = current_block;
  103. iterator = new CustomBlockArrayIterator();
  104. iterator->array = this;
  105. iterator->current_block = first_block;
  106. iterator->current_position = first_block->p_data;
  107. }
  108. bool CustomGrowableBlockArray::IsEmpty()
  109. {
  110. return first_block->next_item == first_block->p_data;
  111. }
  112. void CustomGrowableBlockArray::PushBack(Il2CppObject* value, LivenessState *state)
  113. {
  114. if (current_block->next_item == current_block->p_data + kArrayElementsPerBlock)
  115. {
  116. CustomArrayBlock* new_block = current_block->next_block;
  117. if (current_block->next_block == NULL)
  118. {
  119. new_block = (CustomArrayBlock*)state->reallocateArray(NULL, kBlockSize, state->callback_userdata);
  120. new_block->next_block = NULL;
  121. new_block->prev_block = current_block;
  122. new_block->next_item = new_block->p_data;
  123. current_block->next_block = new_block;
  124. }
  125. current_block = new_block;
  126. }
  127. *current_block->next_item++ = value;
  128. }
  129. Il2CppObject* CustomGrowableBlockArray::PopBack()
  130. {
  131. if (current_block->next_item == current_block->p_data)
  132. {
  133. if (current_block->prev_block == NULL)
  134. return NULL;
  135. current_block = current_block->prev_block;
  136. current_block->next_item = current_block->p_data + kArrayElementsPerBlock;
  137. }
  138. return *--current_block->next_item;
  139. }
  140. void CustomGrowableBlockArray::ResetIterator()
  141. {
  142. iterator->current_block = first_block;
  143. iterator->current_position = first_block->p_data;
  144. }
  145. Il2CppObject* CustomGrowableBlockArray::Next()
  146. {
  147. if (iterator->current_position != iterator->current_block->next_item)
  148. return *iterator->current_position++;
  149. if (iterator->current_block->next_block == NULL)
  150. return NULL;
  151. iterator->current_block = iterator->current_block->next_block;
  152. iterator->current_position = iterator->current_block->p_data;
  153. if (iterator->current_position == iterator->current_block->next_item)
  154. return NULL;
  155. return *iterator->current_position++;
  156. }
  157. void CustomGrowableBlockArray::Clear()
  158. {
  159. CustomArrayBlock *block = first_block;
  160. while (block != NULL)
  161. {
  162. block->next_item = block->p_data;
  163. block = block->next_block;
  164. }
  165. }
  166. void CustomGrowableBlockArray::Destroy(LivenessState *state)
  167. {
  168. CustomArrayBlock *block = first_block;
  169. while (block != NULL)
  170. {
  171. CustomArrayBlock *data_block = block;
  172. block = block->next_block;
  173. state->reallocateArray(data_block, 0, state->callback_userdata);
  174. }
  175. delete iterator;
  176. delete this;
  177. }
  178. LivenessState::LivenessState(Il2CppClass* filter, uint32_t maxCount, Liveness::register_object_callback callback, void*callback_userdata, Liveness::ReallocateArrayCallback reallocateArray) :
  179. all_objects(NULL),
  180. filter(NULL),
  181. process_array(NULL),
  182. callback_userdata(NULL),
  183. filter_callback(NULL),
  184. reallocateArray(reallocateArray),
  185. traverse_depth(0)
  186. {
  187. // construct liveness_state;
  188. // allocate memory for the following structs
  189. // all_objects: contains a list of all referenced objects to be able to clean the vtable bits after the traversal
  190. // process_array. array that contains the objcets that should be processed. this should run depth first to reduce memory usage
  191. // 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.
  192. this->filter = filter;
  193. this->callback_userdata = callback_userdata;
  194. this->filter_callback = callback;
  195. all_objects = new CustomGrowableBlockArray(this);
  196. process_array = new CustomGrowableBlockArray(this);
  197. }
  198. LivenessState::~LivenessState()
  199. {
  200. all_objects->Destroy(this);
  201. process_array->Destroy(this);
  202. }
  203. void LivenessState::Finalize()
  204. {
  205. all_objects->ResetIterator();
  206. Il2CppObject* object = all_objects->Next();
  207. while (object != NULL)
  208. {
  209. CLEAR_OBJ(object);
  210. object = all_objects->Next();
  211. }
  212. }
  213. void LivenessState::Reset()
  214. {
  215. process_array->Clear();
  216. }
  217. void LivenessState::TraverseObjects()
  218. {
  219. Il2CppObject* object = NULL;
  220. traverse_depth++;
  221. while (!process_array->IsEmpty())
  222. {
  223. object = process_array->PopBack();
  224. TraverseGenericObject(object, this);
  225. }
  226. traverse_depth--;
  227. }
  228. void LivenessState::FilterObjects()
  229. {
  230. Il2CppObject* filtered_objects[64];
  231. int32_t num_objects = 0;
  232. Il2CppObject* value = all_objects->Next();
  233. while (value)
  234. {
  235. Il2CppObject* object = value;
  236. if (ShouldProcessValue(object, filter))
  237. filtered_objects[num_objects++] = object;
  238. if (num_objects == 64)
  239. {
  240. filter_callback(filtered_objects, 64, callback_userdata);
  241. num_objects = 0;
  242. }
  243. value = all_objects->Next();
  244. }
  245. if (num_objects != 0)
  246. filter_callback(filtered_objects, num_objects, callback_userdata);
  247. }
  248. void LivenessState::TraverseGenericObject(Il2CppObject* object, LivenessState* state)
  249. {
  250. IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(LivenessState::TraverseGenericObject, "Use GC bitmap when we have one");
  251. #if IL2CPP_HAS_GC_DESCRIPTORS
  252. size_t gc_desc = (size_t)(GET_CLASS(object)->gc_desc);
  253. if (gc_desc & (size_t)1)
  254. TraverseGCDescriptor(object, state);
  255. else
  256. #endif
  257. if (GET_CLASS(object)->rank)
  258. TraverseArray((Il2CppArray*)object, state);
  259. else
  260. TraverseObject(object, state);
  261. }
  262. void LivenessState::TraverseObject(Il2CppObject* object, LivenessState* state)
  263. {
  264. TraverseObjectInternal(object, false, GET_CLASS(object), state);
  265. }
  266. void LivenessState::TraverseGCDescriptor(Il2CppObject* object, LivenessState* state)
  267. {
  268. #define WORDSIZE ((int)sizeof(size_t)*8)
  269. int i = 0;
  270. size_t mask = (size_t)(GET_CLASS(object)->gc_desc);
  271. IL2CPP_ASSERT(mask & (size_t)1);
  272. for (i = 0; i < WORDSIZE - 2; i++)
  273. {
  274. size_t offset = ((size_t)1 << (WORDSIZE - 1 - i));
  275. if (mask & offset)
  276. {
  277. Il2CppObject* val = *(Il2CppObject**)(((char*)object) + i * sizeof(void*));
  278. AddProcessObject(val, state);
  279. }
  280. }
  281. }
  282. bool LivenessState::TraverseObjectInternal(Il2CppObject* object, bool isStruct, Il2CppClass* klass, LivenessState* state)
  283. {
  284. FieldInfo *field;
  285. Il2CppClass *p;
  286. bool added_objects = false;
  287. IL2CPP_ASSERT(object);
  288. if (!klass->size_inited)
  289. {
  290. IL2CPP_ASSERT(isStruct);
  291. return false;
  292. }
  293. // subtract the added offset for the vtable. This is added to the offset even though it is a struct
  294. if (isStruct)
  295. object--;
  296. for (p = klass; p != NULL; p = p->parent)
  297. {
  298. void* iter = NULL;
  299. while ((field = Class::GetFields(p, &iter)))
  300. {
  301. if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
  302. continue;
  303. if (!FieldCanContainReferences(field))
  304. continue;
  305. if (Type::IsStruct(field->type))
  306. {
  307. char* offseted = (char*)object;
  308. offseted += field->offset;
  309. if (Type::IsGenericInstance(field->type))
  310. {
  311. IL2CPP_ASSERT(field->type->data.generic_class->cached_class);
  312. added_objects |= TraverseObjectInternal((Il2CppObject*)offseted, true, field->type->data.generic_class->cached_class, state);
  313. }
  314. else
  315. added_objects |= TraverseObjectInternal((Il2CppObject*)offseted, true, Type::GetClass(field->type), state);
  316. continue;
  317. }
  318. if (field->offset == THREAD_STATIC_FIELD_OFFSET)
  319. {
  320. IL2CPP_ASSERT(0);
  321. }
  322. else
  323. {
  324. Il2CppObject* val = NULL;
  325. Field::GetValue(object, field, &val);
  326. added_objects |= AddProcessObject(val, state);
  327. }
  328. }
  329. }
  330. return added_objects;
  331. }
  332. void LivenessState::TraverseArray(Il2CppArray* array, LivenessState* state)
  333. {
  334. size_t i = 0;
  335. bool has_references;
  336. Il2CppObject* object = (Il2CppObject*)array;
  337. Il2CppClass* element_class;
  338. size_t elementClassSize;
  339. size_t array_length;
  340. IL2CPP_ASSERT(object);
  341. element_class = GET_CLASS(object)->element_class;
  342. has_references = !Class::IsValuetype(element_class);
  343. IL2CPP_ASSERT(element_class->size_inited != 0);
  344. FieldInfo* field;
  345. void* iter = NULL;
  346. while ((field = Class::GetFields(element_class, &iter)))
  347. {
  348. has_references |= FieldCanContainReferences(field);
  349. if (has_references)
  350. break;
  351. }
  352. if (!has_references)
  353. return;
  354. array_length = Array::GetLength(array);
  355. if (element_class->byval_arg.valuetype)
  356. {
  357. size_t items_processed = 0;
  358. elementClassSize = Class::GetArrayElementSize(element_class);
  359. for (i = 0; i < array_length; i++)
  360. {
  361. Il2CppObject* object = (Il2CppObject*)il2cpp_array_addr_with_size(array, (int32_t)elementClassSize, i);
  362. if (TraverseObjectInternal(object, 1, element_class, state))
  363. items_processed++;
  364. // Add 64 objects at a time and then traverse
  365. if (ShouldTraverseObjects(items_processed, state->traverse_depth))
  366. state->TraverseObjects();
  367. }
  368. }
  369. else
  370. {
  371. size_t items_processed = 0;
  372. for (i = 0; i < array_length; i++)
  373. {
  374. Il2CppObject* val = il2cpp_array_get(array, Il2CppObject*, i);
  375. if (AddProcessObject(val, state))
  376. items_processed++;
  377. // Add 64 objects at a time and then traverse
  378. if (ShouldTraverseObjects(items_processed, state->traverse_depth))
  379. state->TraverseObjects();
  380. }
  381. }
  382. }
  383. bool LivenessState::AddProcessObject(Il2CppObject* object, LivenessState* state)
  384. {
  385. if (!object || IS_MARKED(object))
  386. return false;
  387. bool has_references = GET_CLASS(object)->has_references;
  388. if (has_references || ShouldProcessValue(object, state->filter))
  389. {
  390. state->all_objects->PushBack(object, state);
  391. MARK_OBJ(object);
  392. }
  393. // Check if klass has further references - if not skip adding
  394. if (has_references)
  395. {
  396. state->process_array->PushBack(object, state);
  397. return true;
  398. }
  399. return false;
  400. }
  401. bool LivenessState::ShouldProcessValue(Il2CppObject* val, Il2CppClass* filter)
  402. {
  403. Il2CppClass* val_class = GET_CLASS(val);
  404. if (filter && !ClassInlines::HasParentUnsafe(val_class, filter))
  405. return false;
  406. return true;
  407. }
  408. bool LivenessState::FieldCanContainReferences(FieldInfo* field)
  409. {
  410. if (Type::IsStruct(field->type))
  411. return true;
  412. if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL)
  413. return false;
  414. if (field->type->type == IL2CPP_TYPE_STRING)
  415. return false;
  416. return Type::IsReference(field->type);
  417. }
  418. void* Liveness::AllocateStruct(Il2CppClass* filter, int max_object_count, register_object_callback callback, void* userdata, ReallocateArrayCallback reallocateArray)
  419. {
  420. // ensure filter is initialized so we can do fast (and lock free) check HasParentUnsafe
  421. Class::SetupTypeHierarchy(filter);
  422. LivenessState* state = new LivenessState(filter, max_object_count, callback, userdata, reallocateArray);
  423. // no allocations can happen beyond this point
  424. return state;
  425. }
  426. void Liveness::FreeStruct(void* state)
  427. {
  428. LivenessState* lstate = (LivenessState*)state;
  429. delete lstate;
  430. }
  431. void Liveness::Finalize(void* state)
  432. {
  433. LivenessState* lstate = (LivenessState*)state;
  434. lstate->Finalize();
  435. }
  436. void Liveness::FromRoot(Il2CppObject* root, void* state)
  437. {
  438. LivenessState* liveness_state = (LivenessState*)state;
  439. liveness_state->Reset();
  440. liveness_state->process_array->PushBack(root, liveness_state);
  441. liveness_state->TraverseObjects();
  442. //Filter objects and call callback to register found objects
  443. liveness_state->FilterObjects();
  444. }
  445. void Liveness::FromStatics(void* state)
  446. {
  447. LivenessState* liveness_state = (LivenessState*)state;
  448. const il2cpp::utils::dynamic_array<Il2CppClass*>& classesWithStatics = Class::GetStaticFieldData();
  449. liveness_state->Reset();
  450. for (il2cpp::utils::dynamic_array<Il2CppClass*>::const_iterator iter = classesWithStatics.begin();
  451. iter != classesWithStatics.end();
  452. iter++)
  453. {
  454. Il2CppClass* klass = *iter;
  455. FieldInfo *field;
  456. if (!klass)
  457. continue;
  458. if (klass->image == il2cpp_defaults.corlib)
  459. continue;
  460. if (klass->size_inited == 0)
  461. continue;
  462. void* fieldIter = NULL;
  463. while ((field = Class::GetFields(klass, &fieldIter)))
  464. {
  465. if (!vm::Field::IsNormalStatic(field))
  466. continue;
  467. if (!LivenessState::FieldCanContainReferences(field))
  468. continue;
  469. if (Type::IsStruct(field->type))
  470. {
  471. char* offseted = (char*)klass->static_fields;
  472. offseted += field->offset;
  473. if (Type::IsGenericInstance(field->type))
  474. {
  475. IL2CPP_ASSERT(field->type->data.generic_class->cached_class);
  476. LivenessState::TraverseObjectInternal((Il2CppObject*)offseted, true, field->type->data.generic_class->cached_class, liveness_state);
  477. }
  478. else
  479. {
  480. LivenessState::TraverseObjectInternal((Il2CppObject*)offseted, true, Type::GetClass(field->type), liveness_state);
  481. }
  482. }
  483. else
  484. {
  485. Il2CppObject* val = NULL;
  486. Field::StaticGetValue(field, &val);
  487. if (val)
  488. {
  489. LivenessState::AddProcessObject(val, liveness_state);
  490. }
  491. }
  492. }
  493. }
  494. liveness_state->TraverseObjects();
  495. //Filter objects and call callback to register found objects
  496. liveness_state->FilterObjects();
  497. }
  498. } /* namespace vm */
  499. } /* namespace il2cpp */