-
Notifications
You must be signed in to change notification settings - Fork 574
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
Add APIs to read and destroy sections #3377
base: main
Are you sure you want to change the base?
Add APIs to read and destroy sections #3377
Conversation
7578068
to
a2d24fe
Compare
a2d24fe
to
8efda94
Compare
is this basically a copy from wamr-app-framework? |
Yes, I should have mentioned it in the PR description.
We want to be able to free as many resources that are not necessary anymore after module instantiation or loading (especially for constrained embedded devices). |
63e2571
to
edb8046
Compare
core/iwasm/aot/aot_loader.c
Outdated
{ | ||
AOTModule fake_module; // Unused, passed since required by create_sections | ||
return create_sections(&fake_module, buf, size, p_section_list, error_buf, | ||
error_buf_size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here in create_sections
, the section->section_body
may be referring to the buf
, which is supposed to be freed to reduce the footprint after loading, per my understanding, the loading process can be somewhat like:
buf = read_file_to_buffer(..., &size);
wasm_runtime_read_to_sections(buf, size, §ion_result);
wasm_runtime_free(buf);
wasm_runtime_load_from_sections(section_result.section_list, section_result.is_aot, ...);
wasm_runtime_destroy_sections(..); // destroy part of sections
wasm_runtime_instantiate();
...
wasm_runtime_destroy_sections(...); // destroy all of sections
So we should allocate memory for section->section_body
and copy the data from the buf
. How about adding a new argument to create_sections:
static bool
create_sections(AOTModule *module, const uint8 *buf, uint32 size,
bool alloc_mem_for_section_body,
AOTSection **p_section_list, char *error_buf,
uint32 error_buf_size)
And pass true to alloc_mem_for_section_body
, and allocate memory for section->section_body
:
if (section_type == AOT_SECTION_TYPE_TEXT) {
... original code ...
}
else {
...
}
And should also change destroy_sections accordingly, add a new argument to indicate whether to free section->section_body
or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I ran into the problem this morning when I was trying the code from an embedded with a sanitizer and I was getting an use-after-free
error.
I'll adjust the code. Probably worth adding an example showing the usage as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way, I was able to release all wasm sections after module instantiation when testing in fast interpreter mode, including the code section that is left out here https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-mgr/app-manager/module_wasm_app.c#L728-L746
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good, and yes, it would be great if a sample is added. And we can also mention the method in the document Tune the memory usage, it should benefit to some developers of the Embdded/IoT.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, let me do it
cdae702
to
59aafe4
Compare
section->section_body_size = section_size; | ||
if (alloc_mem_for_section_body) { | ||
section->section_body = loader_malloc( | ||
section->section_body_size, error_buf, error_buf_size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as aot, should change destroy_sections in this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it needed? destroy_sections
is only called by load
, not by create_sections
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is to avoid memory leak, destroy_sections
is called when create_sections
failed: some section_bodies may have been allocated when a section body is allocated failed. See L6423.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But when create_sections
is called from L6423 it has alloc_mem_for_section_body
set to false, meaning that no memory has to be freed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know why now, in this function, when creating section failed, it doesn't free the section nodes created like AOT, and the below code in function load will call destroy_sections to free them even when create_sections
failed:
if (!create_sections(buf, size, false, §ion_list, error_buf,
error_buf_size)
|| !load_from_sections(module, section_list, true, error_buf,
error_buf_size)) {
destroy_sections(section_list);
return false;
}
But I think it is not a good coding style, and it will lead to memory leak in the wasm_read_to_sections, which doesn't call destroy_sections when creating sections failed:
return create_sections(buf, size, true, p_section_list, error_buf,
error_buf_size);
My suggestion is to do like aot: call destroy_sections
in the lable fail
when failing to create_sections
, and change return false
in this function to goto fail
. And also add an argument free_section_body
like aot.
And change below code to be like:
if (!create_sections(buf, size, false, §ion_list, error_buf,
error_buf_size))
return false;
if (!load_from_sections(module, section_list, true, error_buf,
error_buf_size)) {
destroy_sections(section_list, false); //free_section_body =
return false;
}
destroy_sections(section_list, false); //free_section_body = false
return true;
e765899
to
d1c132d
Compare
b0b80d6
to
c2ff590
Compare
c2ff590
to
e8187f2
Compare
please update the description then.
if you just want to save some memory, why do you need this complicated api? |
sometimes it's useful to have an api to examine sections. (especially custom sections) |
wasm_runtime_load has been used for a long time, we had better not change its prototype to add an option? And we don't know what the wasm/aot file buf is allocated, normally it can be freed only after wasm_runtime_load, so if we also create sections in wasm_runtime_load and allocate memory for |
we have introduced wasm_runtime_load_ex very recently.
iiuc, there are three memory allocations involved:
with this PR, the peak memory usage is max(1+2, 2+3), right? if the peak memory usage is a problem, it's better to introduce a stream-style api
and/or, maybe we can simply make a user specify a "free the buffer" callback so that some buffers can be freed a bit earlier. |
} | ||
|
||
/* Destroy all sections after instantiation. For AOT, the text sections must | ||
* be kept because referenced during execution */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, an api should not hardcode this kind of implementation details.
Yes, it is good to modify wasm_runtime_load_ex only, we may add an option for wasm_runtime_load_ex to choose whether to create sections, load and free sections. And even add another option of freeing buffer callback to allow wasm_runtime_load_ex to free the buffer, so it can create sections, free wasm/aot file buffer, load and free sections to reduce the peak memory usage.
Yes, the user buffer can be freed after the sections are created.
How about letting the developer pass a read stream callback to read n bytes from the wasm/aot file, somewhat like typedef uint32 (*read_file_callback_t)(char *buf, uint32 count); //read count bytes to buffer
wasm_module_t
wasm_runtime_load_from_stream(read_file_callback_t, char *error_buf, uint32_t error_buf_size); Or just extend wasm_runtime_load_ex to allow developer to pass the read file callback to LoadArgs structure. Then the loader can then read and create sections first (read section type and section size, create section body with section size, and then read data to section body), and then load from sections, then destroy sections after loading. In this way, we can reduce both the peak memory usage and memory usage after loading. |
The specific problem I'm trying to solve is the one described in #3354. I'm using the wasm-c-api and module and module instance are saved into the store, that can't be freed until the end of wasm execution. And the module in the store has a copy of the module binary wasm-micro-runtime/core/iwasm/common/wasm_c_api.c Line 2290 in c0e33f0
By using the APIs described in this PR, I can free the sections after module instantiation. Especially the code and data sections, that can be quite large, saving some memory before starting the wasm execution. |
Actually I was thinking, what if we only save the pointer here wasm-micro-runtime/core/iwasm/common/wasm_c_api.c Lines 2286 to 2292 in c0e33f0
We could specify that behavior in LoadArgs from wasm_module_new_ex .
|
@eloparco it seems to be a common issue so I think we can have a try. And we can also try to enable other methods in the future:
|
i feel that a callback based api in this case is a bit cumbersome to use for many use cases. |
Yes, that is better and it would be great if it supports receiving wasm file buffer from socket, uart or from a disk file. Not sure what the context-based api will be like? |
#3354