wasm_memory.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /*
  2. * Copyright (C) 2019 Intel Corporation. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #include "wasm_runtime_common.h"
  6. #include "bh_platform.h"
  7. #include "mem_alloc.h"
  8. #define BH_ENABLE_MEMORY_PROFILING 0
  9. #if BH_ENABLE_MEMORY_PROFILING != 0
  10. /* Memory profile data of a function */
  11. typedef struct memory_profile {
  12. struct memory_profile *next;
  13. const char *function_name;
  14. const char *file_name;
  15. int line_in_file;
  16. int malloc_num;
  17. int free_num;
  18. int total_malloc;
  19. int total_free;
  20. } memory_profile_t;
  21. /* Memory in use which grows when BH_MALLOC was called
  22. * and decreases when bh_free was called */
  23. static unsigned int memory_in_use = 0;
  24. /* Memory profile data list */
  25. static memory_profile_t *memory_profiles_list = NULL;
  26. /* Lock of the memory profile list */
  27. static korp_mutex profile_lock;
  28. #endif /* end of BH_ENABLE_MEMORY_PROFILING */
  29. typedef enum Memory_Mode {
  30. MEMORY_MODE_UNKNOWN = 0,
  31. MEMORY_MODE_POOL,
  32. MEMORY_MODE_ALLOCATOR
  33. } Memory_Mode;
  34. static Memory_Mode memory_mode = MEMORY_MODE_UNKNOWN;
  35. static mem_allocator_t pool_allocator = NULL;
  36. static void *(*malloc_func)(unsigned int size) = NULL;
  37. static void *(*realloc_func)(void *ptr, unsigned int size) = NULL;
  38. static void (*free_func)(void *ptr) = NULL;
  39. static unsigned int global_pool_size;
  40. static bool
  41. wasm_memory_init_with_pool(void *mem, unsigned int bytes)
  42. {
  43. mem_allocator_t _allocator = mem_allocator_create(mem, bytes);
  44. if (_allocator) {
  45. memory_mode = MEMORY_MODE_POOL;
  46. pool_allocator = _allocator;
  47. #if BH_ENABLE_MEMORY_PROFILING != 0
  48. os_mutex_init(&profile_lock);
  49. #endif
  50. global_pool_size = bytes;
  51. return true;
  52. }
  53. LOG_ERROR("Init memory with pool (%p, %u) failed.\n", mem, bytes);
  54. return false;
  55. }
  56. static bool
  57. wasm_memory_init_with_allocator(void *_malloc_func,
  58. void *_realloc_func,
  59. void *_free_func)
  60. {
  61. if (_malloc_func && _free_func && _malloc_func != _free_func) {
  62. memory_mode = MEMORY_MODE_ALLOCATOR;
  63. malloc_func = _malloc_func;
  64. realloc_func = _realloc_func;
  65. free_func = _free_func;
  66. #if BH_ENABLE_MEMORY_PROFILING != 0
  67. os_mutex_init(&profile_lock);
  68. #endif
  69. return true;
  70. }
  71. LOG_ERROR("Init memory with allocator (%p, %p, %p) failed.\n",
  72. _malloc_func, _realloc_func, _free_func);
  73. return false;
  74. }
  75. bool
  76. wasm_runtime_memory_init(mem_alloc_type_t mem_alloc_type,
  77. const MemAllocOption *alloc_option)
  78. {
  79. if (mem_alloc_type == Alloc_With_Pool)
  80. return wasm_memory_init_with_pool(alloc_option->pool.heap_buf,
  81. alloc_option->pool.heap_size);
  82. else if (mem_alloc_type == Alloc_With_Allocator)
  83. return wasm_memory_init_with_allocator(alloc_option->allocator.malloc_func,
  84. alloc_option->allocator.realloc_func,
  85. alloc_option->allocator.free_func);
  86. else if (mem_alloc_type == Alloc_With_System_Allocator)
  87. return wasm_memory_init_with_allocator(os_malloc, os_realloc, os_free);
  88. else
  89. return false;
  90. }
  91. void
  92. wasm_runtime_memory_destroy()
  93. {
  94. #if BH_ENABLE_MEMORY_PROFILING != 0
  95. os_mutex_destroy(&profile_lock);
  96. #endif
  97. if (memory_mode == MEMORY_MODE_POOL)
  98. mem_allocator_destroy(pool_allocator);
  99. memory_mode = MEMORY_MODE_UNKNOWN;
  100. }
  101. unsigned
  102. wasm_runtime_memory_pool_size()
  103. {
  104. if (memory_mode == MEMORY_MODE_POOL)
  105. return global_pool_size;
  106. else
  107. return 1 * BH_GB;
  108. }
  109. static inline void *
  110. wasm_runtime_malloc_internal(unsigned int size)
  111. {
  112. if (memory_mode == MEMORY_MODE_UNKNOWN) {
  113. LOG_WARNING("wasm_runtime_malloc failed: memory hasn't been initialize.\n");
  114. return NULL;
  115. }
  116. else if (memory_mode == MEMORY_MODE_POOL) {
  117. return mem_allocator_malloc(pool_allocator, size);
  118. }
  119. else {
  120. return malloc_func(size);
  121. }
  122. }
  123. static inline void *
  124. wasm_runtime_realloc_internal(void *ptr, unsigned int size)
  125. {
  126. if (memory_mode == MEMORY_MODE_UNKNOWN) {
  127. LOG_WARNING("wasm_runtime_realloc failed: memory hasn't been initialize.\n");
  128. return NULL;
  129. }
  130. else if (memory_mode == MEMORY_MODE_POOL) {
  131. return mem_allocator_realloc(pool_allocator, ptr, size);
  132. }
  133. else {
  134. if (realloc_func)
  135. return realloc_func(ptr, size);
  136. else
  137. return NULL;
  138. }
  139. }
  140. static inline void
  141. wasm_runtime_free_internal(void *ptr)
  142. {
  143. if (!ptr) {
  144. LOG_WARNING("warning: wasm_runtime_free with NULL pointer\n");
  145. return;
  146. }
  147. if (memory_mode == MEMORY_MODE_UNKNOWN) {
  148. LOG_WARNING("warning: wasm_runtime_free failed: "
  149. "memory hasn't been initialize.\n");
  150. }
  151. else if (memory_mode == MEMORY_MODE_POOL) {
  152. mem_allocator_free(pool_allocator, ptr);
  153. }
  154. else {
  155. free_func(ptr);
  156. }
  157. }
  158. void *
  159. wasm_runtime_malloc(unsigned int size)
  160. {
  161. if (size == 0) {
  162. LOG_WARNING("warning: wasm_runtime_malloc with size zero\n");
  163. /* At lease alloc 1 byte to avoid malloc failed */
  164. size = 1;
  165. }
  166. return wasm_runtime_malloc_internal(size);
  167. }
  168. void *
  169. wasm_runtime_realloc(void *ptr, unsigned int size)
  170. {
  171. return wasm_runtime_realloc_internal(ptr, size);
  172. }
  173. void
  174. wasm_runtime_free(void *ptr)
  175. {
  176. wasm_runtime_free_internal(ptr);
  177. }
  178. #if 0
  179. static uint64 total_malloc = 0;
  180. static uint64 total_free = 0;
  181. void *
  182. wasm_runtime_malloc(unsigned int size)
  183. {
  184. void *ret = wasm_runtime_malloc_internal(size + 8);
  185. if (ret) {
  186. total_malloc += size;
  187. *(uint32 *)ret = size;
  188. return (uint8 *)ret + 8;
  189. }
  190. else
  191. return NULL;
  192. }
  193. void *
  194. wasm_runtime_realloc(void *ptr, unsigned int size)
  195. {
  196. if (!ptr)
  197. return wasm_runtime_malloc(size);
  198. else {
  199. uint8 *ptr_old = (uint8 *)ptr - 8;
  200. uint32 size_old = *(uint32 *)ptr_old;
  201. ptr = wasm_runtime_realloc_internal(ptr_old, size + 8);
  202. if (ptr) {
  203. total_free += size_old;
  204. total_malloc += size;
  205. *(uint32 *)ptr = size;
  206. return (uint8 *)ptr + 8;
  207. }
  208. return NULL;
  209. }
  210. }
  211. void
  212. wasm_runtime_free(void *ptr)
  213. {
  214. if (ptr) {
  215. uint8 *ptr_old = (uint8 *)ptr - 8;
  216. uint32 size_old = *(uint32 *)ptr_old;
  217. total_free += size_old;
  218. wasm_runtime_free_internal(ptr_old);
  219. }
  220. }
  221. void dump_memory_usage()
  222. {
  223. os_printf("Memory usage:\n");
  224. os_printf(" total malloc: %"PRIu64"\n", total_malloc);
  225. os_printf(" total free: %"PRIu64"\n", total_free);
  226. }
  227. #endif
  228. #if BH_ENABLE_MEMORY_PROFILING != 0
  229. void
  230. memory_profile_print(const char *file, int line,
  231. const char *func, int alloc)
  232. {
  233. os_printf("location:%s@%d:used:%d:contribution:%d\n",
  234. func, line, memory_in_use, alloc);
  235. }
  236. void *
  237. wasm_runtime_malloc_profile(const char *file, int line,
  238. const char *func, unsigned int size)
  239. {
  240. void *p = wasm_runtime_malloc(size + 8);
  241. if (p) {
  242. memory_profile_t *profile;
  243. os_mutex_lock(&profile_lock);
  244. profile = memory_profiles_list;
  245. while (profile) {
  246. if (strcmp(profile->function_name, func) == 0
  247. && strcmp(profile->file_name, file) == 0) {
  248. break;
  249. }
  250. profile = profile->next;
  251. }
  252. if (profile) {
  253. profile->total_malloc += size;/* TODO: overflow check */
  254. profile->malloc_num++;
  255. } else {
  256. profile = wasm_runtime_malloc(sizeof(memory_profile_t));
  257. if (!profile) {
  258. os_mutex_unlock(&profile_lock);
  259. bh_memcpy_s(p, size + 8, &size, sizeof(size));
  260. return (char *)p + 8;
  261. }
  262. memset(profile, 0, sizeof(memory_profile_t));
  263. profile->file_name = file;
  264. profile->line_in_file = line;
  265. profile->function_name = func;
  266. profile->malloc_num = 1;
  267. profile->total_malloc = size;
  268. profile->next = memory_profiles_list;
  269. memory_profiles_list = profile;
  270. }
  271. os_mutex_unlock(&profile_lock);
  272. bh_memcpy_s(p, size + 8, &size, sizeof(size));
  273. memory_in_use += size;
  274. memory_profile_print(file, line, func, size);
  275. return (char *)p + 8;
  276. }
  277. return NULL;
  278. }
  279. void
  280. wasm_runtime_free_profile(const char *file, int line,
  281. const char *func, void *ptr)
  282. {
  283. unsigned int size = *(unsigned int *)((char *)ptr - 8);
  284. memory_profile_t *profile;
  285. wasm_runtime_free((char *)ptr - 8);
  286. if (memory_in_use >= size)
  287. memory_in_use -= size;
  288. os_mutex_lock(&profile_lock);
  289. profile = memory_profiles_list;
  290. while (profile) {
  291. if (strcmp(profile->function_name, func) == 0
  292. && strcmp(profile->file_name, file) == 0) {
  293. break;
  294. }
  295. profile = profile->next;
  296. }
  297. if (profile) {
  298. profile->total_free += size;/* TODO: overflow check */
  299. profile->free_num++;
  300. } else {
  301. profile = wasm_runtime_malloc(sizeof(memory_profile_t));
  302. if (!profile) {
  303. os_mutex_unlock(&profile_lock);
  304. return;
  305. }
  306. memset(profile, 0, sizeof(memory_profile_t));
  307. profile->file_name = file;
  308. profile->line_in_file = line;
  309. profile->function_name = func;
  310. profile->free_num = 1;
  311. profile->total_free = size;
  312. profile->next = memory_profiles_list;
  313. memory_profiles_list = profile;
  314. }
  315. os_mutex_unlock(&profile_lock);
  316. }
  317. /**
  318. * Summarize memory usage and print it out
  319. * Can use awk to analyze the output like below:
  320. * awk -F: '{print $2,$4,$6,$8,$9}' OFS="\t" ./out.txt | sort -n -r -k 1
  321. */
  322. void memory_usage_summarize()
  323. {
  324. memory_profile_t *profile;
  325. os_mutex_lock(&profile_lock);
  326. profile = memory_profiles_list;
  327. while (profile) {
  328. os_printf("malloc:%d:malloc_num:%d:free:%d:free_num:%d:%s\n",
  329. profile->total_malloc,
  330. profile->malloc_num,
  331. profile->total_free,
  332. profile->free_num,
  333. profile->function_name);
  334. profile = profile->next;
  335. }
  336. os_mutex_unlock(&profile_lock);
  337. }
  338. #endif /* end of BH_ENABLE_MEMORY_PROFILING */