ems_gc.c 12 KB

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