I have a crash, which is very easy to reproduce。
Finally, I found the root cause of crash! That is the hxcpp’s GC fail to mark a local variable,It’s amazing!
How to reproduce:
- In order to simplify the analysis, let’s modify the openfl-sample BunnyMark’s “Main.hx”:
for (i in 0...1) { // Modify from 100 to 1
addBunny ();
}
- Build the openfl-sample BunnyMark :
openfl build wasm.
Then you can open the html page “index.html” which generated by lime. You’ll find that everything is OK, nothing error.
- Modify lime’s EmscriptenPlatform.hx:
if (!project.defines.exists ("openfl_skip_haxe_build")) { //Add
ProcessHelper.runCommand ("", "haxe", args);
} //Add
This will help us to modify and build those cpp files generated by Haxe.
- Modify BunnyMark’s Bunny.cpp file:
void Bunny_obj::__construct(){
HX_STACKFRAME(&_hx_pos_7e22fcda50e23ba6_16_new)
HXDLIN( 16) super::__construct(0,null(),null(),null(),null(),null(),null(),null());
__hxcpp_collect(); // Add this line
}
The above modification is to force GC (mark-sweep).
- Build again the openfl sample project by:
openfl build wasm -Dopenfl_skip_haxe_build
Then open the BunnyMark’s index.html on the browser, Crash will occur !
Crash Analysis:
- Let’s print the gc log in the immix.cpp:
void MarkConservative(int *inBottom, int *inTop,hx::MarkContext *__inCtx)
{
.......
if ( t==allocObject )
{
GCLOG(" Mark object %p (%p)\n", vptr,ptr); // print gc log
HX_MARK_OBJECT( ((hx::Object *)vptr) );
lastPin = vptr;
info->pin();
}
......
}
- Print the memory address of the local variable which gc-mark is missed.
This local variable is Bunny_obj.
Modify the Main.cpp of BunnyMark:
void Main_obj::addBunny(){
HX_GC_STACKFRAME(&_hx_pos_e47a9afac0942eb9_64_addBunny)
HXLINE( 66) Bunny bunny = Bunny_obj::__alloc( HX_CTX );
printf(“Main_obj::addBunny() bunny->Ptr: %p\n”, bunny.GetPtr()); //Add this line
HXLINE( 67) bunny->set_x(( (Float)(0) ));
HXLINE( 68) bunny->set_y(( (Float)(0) ));
HXLINE( 69) bunny->speedX = (::Math_obj::random() * 5);
HXLINE( 70) bunny->speedY = ((::Math_obj::random() * 5) - ((Float)2.5));
HXLINE( 71) this->bunnies->push(bunny);
HXLINE( 72) this->tilemap->addTile(bunny);
}
Important: Do not print Bunny_obj* in Bunny_obj::__alloc()
- Comare the gc log of the normal version and the gc log of the crash version.
compile Bunny.cpp with emcc -O0: (no crash)
http://kuangyaozong.com/wasm-bunny_cpp-O0/
compile Bunny.cpp with emcc -O1: (crash)
http://kuangyaozong.com/wasm-bunny_cpp-O1/
- The important local variable:
Bunny.cpp:
hx::ObjectPtr<Bunny_obj> Bunny_obj::__alloc(hx::Ctx *_hx_ctx) {
Bunny_obj *__this = (Bunny_obj*)(hx::Ctx::alloc(_hx_ctx, sizeof(Bunny_obj), true, "Bunny"));
*(void **)__this = Bunny_obj::_hx_vtable;
__this->__construct();
return __this;
}
The local variable is __this in Bunny_obj::__alloc().
I found that the Bunny_obj* is missing in GC-Mark. Because the Bunny_obj* is collected by GC , so crash happened。
- Compile Bunny.cpp Using emcc -O0, instead of emcc -O1.
Open emscripten-toolchain.xml, modify like this:
<section unless="HXCPP_OPTIM_LEVEL" >
<set name="HXCPP_OPTIM_LEVEL" value="-O0" />
<set name="HXCPP_OPTIM_LEVEL" value="-g" if="debug" />
</section>
Then build BunnyMark with:
openfl build wasm -Dopenfl_skip_haxe_build
Finally, run the html file, crash disappeared,no error. So emcc -O0 will not crash.
Conclusion:
The MARK process of hxcpp GC miss Mark for some local variable when compile emscripten target with emcc -O1.
Can anyone help me ? What is the reason?