Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to load and run another wasm in a running wasm? #3433

Open
kamylee opened this issue May 14, 2024 · 12 comments
Open

How to load and run another wasm in a running wasm? #3433

kamylee opened this issue May 14, 2024 · 12 comments

Comments

@kamylee
Copy link

kamylee commented May 14, 2024

How to load and run another wasm application in a running wasm?

  1. I tried to load the wasm file in the Native method, but there was no error when calling the init() method in wasm, but I couldn't see the printf output information.
  2. I tried to enable WAMR-BUILD-MULTI-MODULE=1. How to pass a string when calling a submodule from the main module? Can you give an example?
@wenyongh
Copy link
Contributor

Hi, for the first one, could you check whether the native method is called successfully (e.g. add a printf in the native method), and whether the exception is thrown when calling init() method (e.g. call wasm_runtime_get_exception after it)?

For the second one, please refer to samples/multi-module.

@kamylee
Copy link
Author

kamylee commented May 22, 2024

image
1、Create a thread to load the wasm app demo1 and execute init(). At this point, the printf function in the init function works well. However, if the init() function calls a native method NativeA(), which loads and runs a new wasm app demo2, the printf output in the init() method of demo2 does not display, although it runs correctly. I am concerned that there might be other issues, so I wanted to ask.

2.I couldn't find an example of how to pass strings in the multi-module example.

@kamylee
Copy link
Author

kamylee commented May 22, 2024

but it's run ok on ubuntu。
image

@wenyongh
Copy link
Contributor

It is a little confusing that it runs good in Ubuntu while failed in your environment. What target and what platform do you use? And did you create another exec_env to call the app's method?

BTW, for multi-module, maybe you can try wasm_runtime_module_malloc in native method to allocate memory from the second module instance's heap, copy the string from the first module instance to it, and then pass the allocated memory offset to second module instance. Refer to:

https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/embed_wamr.md#pass-buffer-to-wasm-function

@kamylee
Copy link
Author

kamylee commented May 28, 2024

target:windows10
platform:windows10
The compilation and running of wamr are both completed on Windows.

The compilation of wasm is completed using the following:
cmake -G "Unix Makefiles" -DWASI_SDK_PREFIX=E:/WorkSpace/DownLoads/wasi-sdk-21.0.m-mingw/wasi-sdk-21.0+m -DCMAKE_TOOLCHAIN_FILE=E:/WorkSpace/DownLoads/wasi-sdk-21.0.m-mingw/wasi-sdk-21.0+m/share/cmake/wasi-sdk.cmake -DCMAKE_SYSROOT=E:/WorkSpace/DownLoads/wasi-sdk-21.0.m-mingw/wasi-sdk-21.0+m/share/wasi-sysroot ..

Code:
I am calling a native method A within a WASM method. a thread is created In this method A. Within this thread, methods such as wasm_runtime_init_thread_env(), wasm_runtime_init(), bh_read_file_to_buffer(...), wasm_runtime_load(...), wasm_runtime_instantiate(...), wasm_runtime_create_exec_env(...), wasm_runtime_lookup_function(...), wasm_runtime_module_malloc(...), wasm_runtime_call_wasm(.test1..), etc., are used. Is there any issue with this approach?
In function test1,Except for printf not displaying, everything else seems to be working fine.

@wenyongh
Copy link
Contributor

In the thread created by method A, should not call wasm_runtime_init and wasm_runtime_init_thread_env again since they have been inited. Could you try removing them?

@kamylee
Copy link
Author

kamylee commented May 31, 2024

This is the A method:
`
int NativeApi_A(wasm_exec_env_t exec_env, char* strJson)
{
std::thread theadMamr(ThreadLoadWasm, strJson);
theadMamr.detach();
return 1;
}
static int ThreadLoadWasm(char* param,)
{
//wasm_runtime_init_thread_env();
Json root = json::parse((char*)param);
if (root == NULL)
{
wasm_runtime_destroy_thread_env();
return 0;
}
co::print("NativeApi_Load_Wasm:", root.str());

string wasmFile = str::cat("wasm/", root.get("wasmfile").as_c_str()).c_str();

 /* initialize the wasm runtime by default configurations */
//wasm_runtime_init();

uint32_t wasm_file_size;
uint8_t* wasm_file_buf = nullptr;
wasm_file_buf = (uint8_t*)bh_read_file_to_buffer(/*wasm_path*/wasmFile.c_str(), &wasm_file_size);
if (!wasm_file_buf || wasm_file_size <= 0) {
	//printf("open wasm app file [%s] failed.\n", /*wasm_path*/wasm_file);
	DLOG << "open wasm app file" << wasmFile << "failed.";
	wasm_runtime_destroy_thread_env();
	return 0;
}

wasm_module_t wasm_module = NULL;
wasm_module_inst_t wasm_module_inst = NULL;
char error_buf[255] = { 0 };

if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size,
	error_buf, sizeof(error_buf))))
{
	DLOG << "wasm_runtime_load:" << error_buf;
	wasm_runtime_destroy_thread_env();
	return 0;
}

uint32_t stack_size = /*2*1024*/64 * 1024, heap_size = 64 * 1024;
if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, stack_size, heap_size,
	error_buf, sizeof(error_buf))))
{
	DLOG << "wasm_runtime_instantiate:" << error_buf;
	wasm_runtime_destroy_thread_env();
	return 0;
}

wasm_exec_env_t exec_env = NULL;
exec_env = wasm_runtime_create_exec_env(wasm_module_inst, stack_size);
if (!exec_env) {
	//printf("Create wasm execution environment failed.\n");
	DLOG << "wasm_runtime_create_exec_env failed";
	wasm_runtime_destroy_thread_env();
	return 0;
}

wasm_function_inst_t init = NULL;
if (!(init = wasm_runtime_lookup_function(wasm_module_inst, "init")))
{
	DLOG << "The init wasm function is not found.";
	wasm_runtime_destroy_thread_env();
	return 0;
}

int bufLen = strlen((char*)param)+1;
char* buffer1 = NULL;
uint32_t wasmBuffer = wasm_runtime_module_malloc(wasm_module_inst, bufLen, (void**)&buffer1);
if (wasmBuffer != 0)
{
	uint32_t argv[2];
	strncpy(buffer1, (char*)param, bufLen);
	argv[0] = wasmBuffer;     /* pass the buffer address for WASM space */
	argv[1] = bufLen;
	if (!wasm_runtime_call_wasm(exec_env, init, 2, argv))
	{
		const char* errInfo = wasm_runtime_get_exception(wasm_module_inst);
		DLOG << "Native wasm_runtime_call_wasm call test faild.err msg:" << errInfo;
	}
	int retAppId = argv[0];
	wasm_runtime_module_free(wasm_module_inst, wasmBuffer);
}

while (g_bAppIsRuning)
{
	co::sleep(100);
}
//wasm_runtime_destroy_thread_env();

}

`

If remove wasm_runtime_init_thread_env and wasm_runtime_init method,an error will occur when run wasm_runtime_instantiate:
WASM module instantiate failed: Exception: thread signal env not inited

@wenyongh
Copy link
Contributor

Don't remove wasm_runtime_init_thread_env in ThreadLoadWasm as the thread is not created by runtime, and ensure that wasm_runtime_destroy_thread_env is called before ThreadLoadWasm returns. And keep removing wasm_runtime_init and wasm_runtime_destroy int ThreadLoadWasm since we just need to init runtime one time.

BTW, will the first module instance exits before ThreadLoadWasm calls the wasm function? Since char* strJson comes from the module instance's linear memory.

@kamylee
Copy link
Author

kamylee commented May 31, 2024

Is it better to create threads using the 'wasm_runtime_spawn_thread' function? Maybe the printf method can display msg since the first module can use printf to display msg.

The first module will not exit before ThreadLoadWasm calls the wasm function.

@wenyongh
Copy link
Contributor

No, if the thread is to run a function of the same module instance that the thread belongs to, I think we had better use wasm_runtime_spawn_thread, otherwise had better create the thread by host embedder.

Could you print string param and buffer1 to ensure they are correct in the code below before calling wasm function:

int bufLen = strlen((char*)param)+1;
char* buffer1 = NULL;
uint32_t wasmBuffer = wasm_runtime_module_malloc(wasm_module_inst, bufLen, (void**)&buffer1);
if (wasmBuffer != 0)
{
	uint32_t argv[2];
	strncpy(buffer1, (char*)param, bufLen);
	argv[0] = wasmBuffer;     /* pass the buffer address for WASM space */
	argv[1] = bufLen;
	if (!wasm_runtime_call_wasm(exec_env, init, 2, argv))

BTW, did you add -Wl,--export=malloc -Wl,--export=free when building the wasm file? If not using the -nostdlib flag, had better add them to let runtime call the malloc/free function exported instead of inserting a host managed heap into the linear memory.

@kamylee
Copy link
Author

kamylee commented May 31, 2024

image
Print well before calling wasm function.

But print nothing in second wasm module. But print ok with native NativeApi_Print() function.
image

int NativeApi_Print(wasm_exec_env_t exec_env,char* msg) { printf("%s\n",msg); return 1; }

I have added them:
target_link_options(demoS7.wasm PRIVATE LINKER:--export=__heap_base LINKER:--export=__data_end LINKER:--export=malloc LINKER:--export=free LINKER:--export=init LINKER:--export=onMessage LINKER:--export=onDataRecv LINKER:--shared-memory,--max-memory=10485760 LINKER:--no-check-features LINKER:--allow-undefined )

@wenyongh
Copy link
Contributor

Could you check whether the pointers of param are the same to ensure that the pointer is correctly passed into NativeApi_Print? e.g. in ThreadLoadWasm, before calling wasm function, and in NativeApi_Print.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants