ems_gc.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /*
  2. * Copyright (C) 2022 Tencent Corporation. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #include "ems/ems_gc.h"
  6. #include "ems_gc_internal.h"
  7. #define GB (1 << 30UL)
  8. #define MARK_NODE_OBJ_CNT 256
  9. #if WASM_ENABLE_GC != 0
  10. /* mark node is used for gc marker*/
  11. typedef struct mark_node_struct {
  12. /* number of to-expand objects can be saved in this node */
  13. gc_size_t cnt;
  14. /* the first unused index */
  15. uint32 idx;
  16. /* next node on the node list */
  17. struct mark_node_struct *next;
  18. /* the actual to-expand objects list */
  19. gc_object_t set[MARK_NODE_OBJ_CNT];
  20. } mark_node_t;
  21. /**
  22. * Alloc a mark node from the native heap
  23. *
  24. * @return a valid mark node if success, NULL otherwise
  25. */
  26. static mark_node_t *
  27. alloc_mark_node(void)
  28. {
  29. mark_node_t *ret = (mark_node_t *)BH_MALLOC(sizeof(mark_node_t));
  30. if (!ret) {
  31. LOG_ERROR("alloc a new mark node failed");
  32. return NULL;
  33. }
  34. ret->cnt = sizeof(ret->set) / sizeof(ret->set[0]);
  35. ret->idx = 0;
  36. ret->next = NULL;
  37. return ret;
  38. }
  39. /* Free a mark node to the native heap
  40. *
  41. * @param node the mark node to free, should not be NULL
  42. */
  43. static void
  44. free_mark_node(mark_node_t *node)
  45. {
  46. bh_assert(node);
  47. BH_FREE((gc_object_t)node);
  48. }
  49. /**
  50. * Sweep phase of mark_sweep algorithm
  51. * @param heap the heap to sweep, should be a valid instance heap
  52. * which has already been marked
  53. */
  54. static void
  55. sweep_instance_heap(gc_heap_t *heap)
  56. {
  57. hmu_t *cur = NULL, *end = NULL, *last = NULL;
  58. hmu_type_t ut;
  59. gc_size_t size;
  60. int i, lsize;
  61. #if GC_STAT_DATA != 0
  62. gc_size_t tot_free = 0;
  63. #endif
  64. bh_assert(gci_is_heap_valid(heap));
  65. cur = (hmu_t *)heap->base_addr;
  66. last = NULL;
  67. end = (hmu_t *)((char *)heap->base_addr + heap->current_size);
  68. /* reset KFC */
  69. lsize =
  70. (int)(sizeof(heap->kfc_normal_list) / sizeof(heap->kfc_normal_list[0]));
  71. for (i = 0; i < lsize; i++) {
  72. heap->kfc_normal_list[i].next = NULL;
  73. }
  74. heap->kfc_tree_root->right = NULL;
  75. heap->root_set = NULL;
  76. while (cur < end) {
  77. ut = hmu_get_ut(cur);
  78. size = hmu_get_size(cur);
  79. bh_assert(size > 0);
  80. if (ut == HMU_FC || ut == HMU_FM
  81. || (ut == HMU_VO && hmu_is_vo_freed(cur))
  82. || (ut == HMU_WO && !hmu_is_wo_marked(cur))) {
  83. /* merge previous free areas with current one */
  84. if (!last)
  85. last = cur;
  86. if (ut == HMU_WO) {
  87. /* Invoke registered finalizer */
  88. gc_object_t cur_obj = hmu_to_obj(cur);
  89. if (gct_vm_get_extra_info_flag(cur_obj)) {
  90. extra_info_node_t *node = gc_search_extra_info_node(
  91. (gc_handle_t)heap, cur_obj, NULL);
  92. bh_assert(node);
  93. node->finalizer(node->obj, node->data);
  94. gc_unset_finalizer((gc_handle_t)heap, cur_obj);
  95. }
  96. }
  97. }
  98. else {
  99. /* current block is still live */
  100. if (last) {
  101. #if GC_STAT_DATA != 0
  102. tot_free += (char *)cur - (char *)last;
  103. #endif
  104. gci_add_fc(heap, last, (char *)cur - (char *)last);
  105. hmu_mark_pinuse(last);
  106. last = NULL;
  107. }
  108. if (ut == HMU_WO) {
  109. /* unmark it */
  110. hmu_unmark_wo(cur);
  111. }
  112. }
  113. cur = (hmu_t *)((char *)cur + size);
  114. }
  115. bh_assert(cur == end);
  116. if (last) {
  117. #if GC_STAT_DATA != 0
  118. tot_free += (char *)cur - (char *)last;
  119. #endif
  120. gci_add_fc(heap, last, (char *)cur - (char *)last);
  121. hmu_mark_pinuse(last);
  122. }
  123. #if GC_STAT_DATA != 0
  124. heap->total_gc_count++;
  125. heap->total_free_size = tot_free;
  126. if ((heap->current_size - tot_free) > heap->highmark_size)
  127. heap->highmark_size = heap->current_size - tot_free;
  128. gc_update_threshold(heap);
  129. #endif
  130. }
  131. /**
  132. * Add a to-expand node to the to-expand list
  133. *
  134. * @param heap should be a valid instance heap
  135. * @param obj should be a valid wo inside @heap
  136. *
  137. * @return GC_ERROR if there is no more resource for marking,
  138. * GC_SUCCESS if success
  139. */
  140. static int
  141. add_wo_to_expand(gc_heap_t *heap, gc_object_t obj)
  142. {
  143. mark_node_t *mark_node = NULL, *new_node = NULL;
  144. hmu_t *hmu = NULL;
  145. bh_assert(obj);
  146. hmu = obj_to_hmu(obj);
  147. bh_assert(gci_is_heap_valid(heap));
  148. bh_assert((gc_uint8 *)hmu >= heap->base_addr
  149. && (gc_uint8 *)hmu < heap->base_addr + heap->current_size);
  150. bh_assert(hmu_get_ut(hmu) == HMU_WO);
  151. if (hmu_is_wo_marked(hmu))
  152. return GC_SUCCESS; /* already marked*/
  153. mark_node = (mark_node_t *)heap->root_set;
  154. if (!mark_node || mark_node->idx == mark_node->cnt) {
  155. new_node = alloc_mark_node();
  156. if (!new_node) {
  157. LOG_ERROR("can not add obj to mark node because of mark node "
  158. "allocation failed");
  159. return GC_ERROR;
  160. }
  161. new_node->next = mark_node;
  162. heap->root_set = new_node;
  163. mark_node = new_node;
  164. }
  165. mark_node->set[mark_node->idx++] = obj;
  166. hmu_mark_wo(hmu);
  167. return GC_SUCCESS;
  168. }
  169. /* Check ems_gc.h for description*/
  170. int
  171. gc_add_root(void *heap_p, gc_object_t obj)
  172. {
  173. gc_heap_t *heap = (gc_heap_t *)heap_p;
  174. hmu_t *hmu = NULL;
  175. if (!obj) {
  176. LOG_ERROR("gc_add_root with NULL obj");
  177. return GC_ERROR;
  178. }
  179. hmu = obj_to_hmu(obj);
  180. if (!gci_is_heap_valid(heap)) {
  181. LOG_ERROR("vm_get_gc_handle_for_current_instance returns invalid heap");
  182. return GC_ERROR;
  183. }
  184. if (!((gc_uint8 *)hmu >= heap->base_addr
  185. && (gc_uint8 *)hmu < heap->base_addr + heap->current_size)) {
  186. LOG_ERROR("Obj is not a object in current instance heap");
  187. return GC_ERROR;
  188. }
  189. if (hmu_get_ut(hmu) != HMU_WO) {
  190. LOG_ERROR("Given objecti s not wo");
  191. return GC_ERROR;
  192. }
  193. if (add_wo_to_expand(heap, obj) != GC_SUCCESS) {
  194. heap->is_fast_marking_failed = 1;
  195. return GC_ERROR;
  196. }
  197. return GC_SUCCESS;
  198. }
  199. /**
  200. * Unmark all marked objects to do rollback
  201. *
  202. * @param heap the heap to do rollback, should be a valid instance heap
  203. */
  204. static void
  205. rollback_mark(gc_heap_t *heap)
  206. {
  207. mark_node_t *mark_node = NULL, *next_mark_node = NULL;
  208. hmu_t *cur = NULL, *end = NULL;
  209. hmu_type_t ut;
  210. gc_size_t size;
  211. bh_assert(gci_is_heap_valid(heap));
  212. /* roll back*/
  213. mark_node = (mark_node_t *)heap->root_set;
  214. while (mark_node) {
  215. next_mark_node = mark_node->next;
  216. free_mark_node(mark_node);
  217. mark_node = next_mark_node;
  218. }
  219. heap->root_set = NULL;
  220. /* then traverse the heap to unmark all marked wos*/
  221. cur = (hmu_t *)heap->base_addr;
  222. end = (hmu_t *)((char *)heap->base_addr + heap->current_size);
  223. while (cur < end) {
  224. ut = hmu_get_ut(cur);
  225. size = hmu_get_size(cur);
  226. if (ut == HMU_WO && hmu_is_wo_marked(cur)) {
  227. hmu_unmark_wo(cur);
  228. }
  229. cur = (hmu_t *)((char *)cur + size);
  230. }
  231. bh_assert(cur == end);
  232. }
  233. /**
  234. * Reclaim GC instance heap
  235. *
  236. * @param heap the heap to reclaim, should be a valid instance heap
  237. *
  238. * @return GC_SUCCESS if success, GC_ERROR otherwise
  239. */
  240. static int
  241. reclaim_instance_heap(gc_heap_t *heap)
  242. {
  243. mark_node_t *mark_node = NULL;
  244. int idx = 0, j = 0;
  245. bool ret, is_compact_mode = false;
  246. gc_object_t obj = NULL, ref = NULL;
  247. hmu_t *hmu = NULL;
  248. gc_uint32 ref_num = 0, ref_start_offset = 0, size = 0, offset = 0;
  249. gc_uint16 *ref_list = NULL;
  250. bh_assert(gci_is_heap_valid(heap));
  251. heap->root_set = NULL;
  252. #if WASM_ENABLE_THREAD_MGR == 0
  253. if (!heap->exec_env)
  254. return GC_SUCCESS;
  255. ret = gct_vm_begin_rootset_enumeration(heap->exec_env, heap);
  256. #else
  257. if (!heap->cluster)
  258. return GC_SUCCESS;
  259. ret = gct_vm_begin_rootset_enumeration(heap->cluster, heap);
  260. #endif
  261. if (!ret)
  262. return GC_ERROR;
  263. #if BH_ENABLE_GC_VERIFY != 0
  264. /* no matter whether the enumeration is successful or not, the data
  265. collected should be checked at first */
  266. mark_node = (mark_node_t *)heap->root_set;
  267. while (mark_node) {
  268. /* all nodes except first should be full filled */
  269. bh_assert(mark_node == (mark_node_t *)heap->root_set
  270. || mark_node->idx == mark_node->cnt);
  271. /* all nodes should be non-empty */
  272. bh_assert(mark_node->idx > 0);
  273. for (idx = 0; idx < (int)mark_node->idx; idx++) {
  274. obj = mark_node->set[idx];
  275. hmu = obj_to_hmu(obj);
  276. bh_assert(hmu_is_wo_marked(hmu));
  277. bh_assert((gc_uint8 *)hmu >= heap->base_addr
  278. && (gc_uint8 *)hmu
  279. < heap->base_addr + heap->current_size);
  280. }
  281. mark_node = mark_node->next;
  282. }
  283. #endif
  284. /* TODO: when fast marking failed, we can still do slow
  285. marking, currently just simply roll it back. */
  286. if (heap->is_fast_marking_failed) {
  287. LOG_ERROR("enumerate rootset failed");
  288. LOG_ERROR("all marked wos will be unmarked to keep heap consistency");
  289. rollback_mark(heap);
  290. heap->is_fast_marking_failed = 0;
  291. return GC_ERROR;
  292. }
  293. /* the algorithm we use to mark all objects */
  294. /* 1. mark rootset and organize them into a mark_node list (last marked
  295. * roots at list header, i.e. stack top) */
  296. /* 2. in every iteration, we use the top node to expand*/
  297. /* 3. execute step 2 till no expanding */
  298. /* this is a BFS & DFS mixed algorithm, but more like DFS */
  299. mark_node = (mark_node_t *)heap->root_set;
  300. while (mark_node) {
  301. heap->root_set = mark_node->next;
  302. /* note that mark_node->idx may change in each loop */
  303. for (idx = 0; idx < (int)mark_node->idx; idx++) {
  304. obj = mark_node->set[idx];
  305. hmu = obj_to_hmu(obj);
  306. size = hmu_get_size(hmu);
  307. if (!gct_vm_get_wasm_object_ref_list(obj, &is_compact_mode,
  308. &ref_num, &ref_list,
  309. &ref_start_offset)) {
  310. LOG_ERROR("mark process failed because failed "
  311. "vm_get_wasm_object_ref_list");
  312. break;
  313. }
  314. if (ref_num >= 2U * GB) {
  315. LOG_ERROR("Invalid ref_num returned");
  316. break;
  317. }
  318. if (is_compact_mode) {
  319. for (j = 0; j < (int)ref_num; j++) {
  320. offset = ref_start_offset + j * sizeof(void *);
  321. bh_assert(offset + sizeof(void *) < size);
  322. ref = *(gc_object_t *)(((gc_uint8 *)obj) + offset);
  323. if (ref == NULL_REF)
  324. continue; /* NULL REF */
  325. if (add_wo_to_expand(heap, ref) == GC_ERROR) {
  326. LOG_ERROR("add_wo_to_expand failed");
  327. break;
  328. }
  329. }
  330. if (j < (int)ref_num)
  331. break;
  332. }
  333. else {
  334. for (j = 0; j < (int)ref_num; j++) {
  335. offset = ref_list[j];
  336. bh_assert(offset + sizeof(void *) < size);
  337. ref = *(gc_object_t *)(((gc_uint8 *)obj) + offset);
  338. if (ref == NULL_REF)
  339. continue; /* NULL REF */
  340. if (add_wo_to_expand(heap, ref) == GC_ERROR) {
  341. LOG_ERROR("mark process failed");
  342. break;
  343. }
  344. }
  345. if (j < (int)ref_num)
  346. break;
  347. }
  348. }
  349. if (idx < (int)mark_node->idx)
  350. break; /* not yet done */
  351. /* obj's in mark_node are all expanded */
  352. free_mark_node(mark_node);
  353. mark_node = heap->root_set;
  354. }
  355. if (mark_node) {
  356. LOG_ERROR("mark process is not successfully finished");
  357. free_mark_node(mark_node);
  358. /* roll back is required */
  359. rollback_mark(heap);
  360. return GC_ERROR;
  361. }
  362. /* now sweep */
  363. sweep_instance_heap(heap);
  364. (void)size;
  365. return GC_SUCCESS;
  366. }
  367. /**
  368. * Do GC on given heap
  369. *
  370. * @param the heap to do GC, should be a valid heap
  371. *
  372. * @return GC_SUCCESS if success, GC_ERROR otherwise
  373. */
  374. int
  375. gci_gc_heap(void *h)
  376. {
  377. int ret = GC_ERROR;
  378. gc_heap_t *heap = (gc_heap_t *)h;
  379. bh_assert(gci_is_heap_valid(heap));
  380. LOG_VERBOSE("#reclaim instance heap %p", heap);
  381. gct_vm_gc_prepare();
  382. gct_vm_mutex_lock(&heap->lock);
  383. heap->is_doing_reclaim = 1;
  384. ret = reclaim_instance_heap(heap);
  385. heap->is_doing_reclaim = 0;
  386. gct_vm_mutex_unlock(&heap->lock);
  387. gct_vm_gc_finished();
  388. LOG_VERBOSE("#reclaim instance heap %p done", heap);
  389. #if BH_ENABLE_GC_VERIFY != 0
  390. gci_verify_heap(heap);
  391. #endif
  392. #if GC_STAT_SHOW != 0
  393. gc_show_stat(heap);
  394. gc_show_fragment(heap);
  395. #endif
  396. return ret;
  397. }
  398. int
  399. gc_is_dead_object(void *obj)
  400. {
  401. return !hmu_is_wo_marked(obj_to_hmu(obj));
  402. }
  403. #else
  404. int
  405. gci_gc_heap(void *h)
  406. {
  407. (void)h;
  408. return GC_ERROR;
  409. }
  410. #endif /* end of WASM_ENABLE_GC != 0 */