COMMUNITY

Crash because hxcpp's GC fail to mark local variable

emscripten
haxe-cpp

(Yzkuang) #1

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:

  1. 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 ();			
   }
  1. 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.

  1. 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.

  1. 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).

  1. 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:

  1. 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();
               }
   ......
}
  1. 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()

  1. 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/

  1. 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-&gt;__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。

  1. 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?