emscripten-core / emscripten-core/emscripten
Memory no leak by asan but still increase with protobuf
- Dominant language
- C++
- Stars
- 27.6k
- Forks
- 3.6k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 105
Description
**Version of emscripten/emsdk:**
3.1.23, 3.1.41 , I tried many versions.
**Full link command:**
```starlark
cc_binary(
name = "modules",
copts = ["--bind"],
linkopts = [
"-fsanitize=address",
"-g2",
"-flto",
"--bind",
"--closure 1",
"-s MALLOC=emmalloc",
"-s ALLOW_MEMORY_GROWTH=0",
"-s ENVIRONMENT=web,worker",
"-s MODULARIZE=1",
"-s EXPORT_NAME='Modules'",
"-s INITIAL_MEMORY=20MB",
"-s TOTAL_MEMORY=40MB",
"-s ASSERTIONS=1",
"-s USE_ES6_IMPORT_META=1",
"-s EXPORT_ES6=1",
"-s USE_PTHREADS=0",
"-s WASM=1",
"-s NODEJS_CATCH_EXIT=0",
"-s EXIT_RUNTIME=1",
],
deps = [":modules_cc_library"],
)
```
I use emscripten-core/emsdk in bazel, so the `-v` seems not work.
**Use case**
My program fetch protobuf data every 100ms, I use wasm to parse data and calculate them. Recently I found the memory is leak slowly, so I try to find it out with AddressSanitizer.
With `-g2` flag, memory in debug version increase more faster. I isolate my code, only parse and delete, the memory still increase.
**code**
1. modules.cc
```cpp
#include
#include "planning/proto/planning_result.pb.h"
#include "emscripten.h"
#include "emscripten/bind.h"
EMSCRIPTEN_BINDINGS(modules) {
emscripten::function("doRecoverableLeakCheck", &__lsan_do_recoverable_leak_check);
emscripten::class_>("PRProto")
.constructor<>()
.function("Clear", &planning::proto::PlanningResult::Clear);
emscripten::class_("google::protobuf::MessageLite")
.function("ParseFromString", &google::protobuf::MessageLite::ParseFromString);
}
int main() {
emscripten_set_main_loop([]() {}, 1, 1);
return 0;
}
```
2. modules.js
```js
import Modules from 'modules/wasm_modules.js'; // compile result of emscripten
class ModulesManager {
constructor() {
this.modules = null;
}
get = async () => {
if (!this.modules) {
this.modules = await Modules();
}
return this.modules;
};
}
const MODULES = new ModulesManager();
export default MODULES;
```
3. app.js
```js
// this function is called as websocket.onmessage(e => updatePlanningResult(e.data));
updatePlanningResult(message) {
return Modules.get().then(modules => {
const pr = new modules.PRProto();
try {
pr.ParseFromString(message);
} finally {
pr.delete();
}
if (this.trace_count === 0) {
modules.doRecoverableLeakCheck();
}
this.trace_count = (this.trace_count + 1) % 50;
return;
}
}
```
**What I had tried**
1. Address Sanitizer says there is leak if I delete `pr.delete()`. After I add `pr.delete()`, asan says thereis no leak at pr. The only leak is Embind because the runtime is still alive. Things seems no problem.

2. But the memory of progress Chrome still increase, after I disable memory growth, OOM will happen in several seconds, it says `ParseFromString` need enlarge memory.

**What I guess**
1. It seems the memory which `pr.delete()` is not be re-used to new `pr.ParseFromString(message)` call, or emscripten didn't know the memory is deleted then try to enlarge the heap for new call.
2. The input of `ParseFromString` is `std::string&`, while `message` in Javascript is ArrayBuffer. What will emscripten do? Will it construct a new `std::string` instance implicit, and leak is the std::string instance?
3. I saw some post says the `protoc` should be also compiled with `emcc`, I didn't do this because it's hard to do in both bazel rules chains and run it in nodejs. Did this matters?
Please give some advice.
Contributor guide
Assessment
This issue has not been assessed yet.