Stream: wamr

Topic: Memory model


view this post on Zulip Alexandru Ene (May 27 2021 at 22:05):

Is there any way to make WAMR allocate until it runs out of _actual_ memory? The comment on heap makes it seem like the param passed in here is some additional memory:

 * @param heap_size the default heap size of the module instance, a heap will
 *        be created besides the app memory space. Both wasm app and native
 *        function can allocate memory from the heap.

However, I'm pretty sure it fails the assert in weasm_enlarge_memory() because it tries to enlarge it pass that limit.

view this post on Zulip Alexandru Ene (May 27 2021 at 22:21):

A good additional question is what is an app in this context? Is app the host program that embeds a wasm VM?

It also mentions the default heap size. It seems like it should grow, but it doesn't.

view this post on Zulip Alexandru Ene (May 27 2021 at 22:39):

Even more weirdly, it fails the assert in ems_kfc.c at line 203 bh_assert(cur == end); in gc_migrate in an intermitent manner, so it seems like this is something other than hitting the memory limit?

view this post on Zulip lum1n0us (Jun 02 2021 at 07:26):

the memory layout of WAMR likes:

linear memory
data operand stack app memory heap enlarged heap

view this post on Zulip Wenyong Huang (Jun 03 2021 at 03:13):

@Alexandru Ene if you want to make WAMR allocate until out of actual memory, there are several ways:
(1) Set the maximum size for wasm app's linear memory, e.g. for wasi-sdk or emcc, add -Wl,--max-memory=value option;
(2) Init the runtime memory allocator with pool mode (Alloc_With_Pool), provide a global heap buffer for runtime to allocate memory from it but not from system memory. For example, you can set macro USE_GLOBAL_HEAP_BUF to 1 in product-mini/platforms/linux/main.c to enable it. Ref to document of wasm_runtime_full_init and RuntimeInitArgs for more details.

And you want to disable the app memory (app heap with ems memory allocator), please export the malloc/free function, e.g. for wasi-sdk and emcc, add -Wl,--export=malloc -Wl,--export=free, so the wasm_runtime_module_malloc from native will allocate memory from the libc's heap.

view this post on Zulip Alexandru Ene (Jun 03 2021 at 18:05):

This is really useful. I have found that setting the heap size to various sizes has helped avoid the problem I describe with that assertion triggering. Is there a requirement that the heap size to be a multiple of X? I didn't see anything written down, but the same WASM code works with a heap size of 16*1024 but not 512 * 1024 (or some larger value but I don't remember what I tried at the time).

I didn't make use of the WAMR heap at all, the code just did some normal allocations in the WASM module itself.

view this post on Zulip Alexandru Ene (Jun 03 2021 at 19:06):

I will set some time aside, put some breakpoints and figure out what's happening. I didn't have time to root cause what was triggering that assert. Thanks for the explanations!

view this post on Zulip Wenyong Huang (Jun 09 2021 at 03:35):

If wasm app doesn't export malloc/free function, the app heap will be inserted into linear memory, which enlarges the __heap_base global of libc heap if it is exported or enlarges the initial linear memory size to reserve enough memory space for the app heap. The app heap will be initialized when setting up the linear memory space, also the libc heap will be initialized when the wasm app starts by special wasm function, e.g. the _start function. And the initialization of the libc heap depends on the value __heap_base global, unfortunately, the wasm-sdk compiler doesn't generate the bytecode to read the __heap_base global value (e.g. global.get __heap_base), it just hardcodes the initial constant value of __heap_base in the bytecode, so the initialization of libc heap may overwritten the data in the app heap, in other words, the app heap might be corrupted. And when runtime detects the app heap is corrupted, it reports "out of memory" when allocating memory from app heap.

So please had better use only one of them: only enable app heap, and disable libc heap, for example, hack wasi-sdk's libc.a, remove dlmalloc.o from it; or only enable libc heap by export malloc/free function for runtime, so native side can use wasm_runtime_module_malloc to allocate memory from libc heap.

view this post on Zulip Alexandru Ene (Jun 09 2021 at 12:35):

Right, so I think what confused me is that i was using wasm_engine_new that allocates with system malloc from what i see in the implementation, but then, wasm_runtime_instantiate has a heap value that one could provide:

wasm_runtime_instantiate(WASMModuleCommon *module,
                         uint32 stack_size, uint32 heap_size,
                         char *error_buf, uint32 error_buf_size)

So this made unclear to me what the correct way would be to use that parameter. It seems that this setup is correct:

wasm_engine_new();
wasm_runtime_instantiate(module, SOME_STACK_SIZE, 0 /*heap size*/, buff, buff_size);

I wonder how I could add -Wl,--export=malloc -Wl,--export=free for a rust program. It should probably be somewhere in linker args.

view this post on Zulip Alexandru Ene (Jun 09 2021 at 12:49):

Actually now that I wrote that I think i can just export it manually and forward the call to some memory allocator in the rust side of the wasm code)

view this post on Zulip Wenyong Huang (Jun 11 2021 at 01:07):

There are two types of data: (1) the runtime internal data, e.g. wasm module, wasm module instance, wasm function instance, (2) the wasm app internal data. So there there are two memory allocator(or two heaps): one is for runtime internal data, we usually call it global heap or global memory allocator, the other is for wasm app internal data, we usually call it app private heap or app private memory allocator.

For the global memory allocator, runtime provides three modes to initialize it: (1) allocate memory from a global buffer provided by runtime embedder, (2) allocate from the system allocator, or os_malloc function implemented in core/shared/platform layer, (3) allocate from the memory allocate function provided by runtime embedder. Ref to wasm_runtime_full_init and RuntimeInitArgs in core/iwasm/include/wasm_export.h for more details. And the runtime embedder can call wasm_runtime_malloc to allocate memory from the global memory allocator.
So the wasm_engine_new here initializes the global memory allocator, the mode is system allocator, meaning to use os_malloc to allocate the memory.

For the app private memory allocator, as explained upper, there might be two memory allocators also, (1) one is initialized by runtime with wasm_runtime_instantiate(..heap_size..), we usually call it app heap, which is ungrowable, (2) the other is initialized by wasm app itself, e.g. libc's malloc func, typescript's __new func, we usually call it libc heap, which is usually growable. And if malloc/free or __new/__release functions are exported, runtime won't create app heap, the wasm_runtime_module_malloc func allocates memory from the export malloc or __new func. If not exported, runtime inserts and initializes app heap according to __heap_base or the linear memory page count, the wasm_runtime_module_malloc allocates memory from the app heap inserted.

For the rust program, no malloc related function is exported, but __heap_base global is exported, wasm runtime inserts and initializes the app heap successfully, and wasm_runtime_module_malloc works. From our testing, the rust application works well. Do you have any issues in running rust program?

view this post on Zulip Alexandru Ene (Jun 11 2021 at 09:10):

No, now it works just fine, I was just trying to understand more about how memory works. This is really useful.


Last updated: Dec 23 2024 at 12:05 UTC