pd-rs/crankstart

'Life' example segfaults simulator

tjkirch opened this issue · 2 comments

When I try to run the life example in the simulator, I get this:

> crank build --release --example life --run
    Finished release [optimized] target(s) in 0.02s
15:18:06: SDK: /home/tjk/code/playdate/PlaydateSDK-1.13.1-beta2-Linux-x86
15:18:06: Release: 1.13.1-beta.2
15:18:06: CMD: /home/tjk/code/playdate/crankstart/target/Life.pdx
15:18:06: Loading: /home/tjk/code/playdate/crankstart/target/Life.pdx/
Loading C API game: /home/tjk/code/playdate/crankstart/target/Life.pdx/pdex.so
15:18:06: Loading: OK
Error: open failed with error ExitStatus(unix_wait_status(11))

The same segfault happens with the 1.13.0 and 1.13.1-beta2 SDKs. This is on Linux with crankstart 8178447.

gdb shows Thread 1 "PlaydateSimulat" received signal SIGSEGV, Segmentation fault. with the following backtrace:

(gdb) bt
#0  0x00007fffec2b0c82 in life::game_setup::update () from target/Life.pdx/pdex.so
#1  0x00005555559bafd9 in pd_update ()
#2  0x0000555555941057 in sim_update () at ../source/simulator.c:676
#3  0x00005555558d7941 in MainFrame::UpdateSim (this=0x555556cd79e0) at ../source/mainframe.cpp:3259
#4  0x00005555558d7c0d in MainFrame::RenderFrame (this=0x555556cd79e0) at ../source/mainframe.cpp:3169
#5  0x0000555555e416a1 in wxEvtHandler::ProcessEventIfMatchesId(wxEventTableEntryBase const&, wxEvtHandler*, wxEvent&) ()
#6  0x0000555555e41b3e in wxEvtHandler::SearchDynamicEventTable(wxEvent&) ()
#7  0x0000555555e41f34 in wxEvtHandler::TryHereOnly(wxEvent&) ()
#8  0x0000555555e41fdf in wxEvtHandler::ProcessEventLocally(wxEvent&) ()
#9  0x0000555555e420e1 in wxEvtHandler::ProcessEvent(wxEvent&) ()
#10 0x0000555555e43198 in wxEvtHandler::ProcessPendingEvents() ()
#11 0x0000555555d24c37 in wxAppConsoleBase::ProcessPendingEvents() ()
#12 0x0000555555bbf87d in wxApp::DoIdle() ()
#13 0x0000555555bbf987 in wxapp_idle_callback ()
#14 0x00007ffff7167a2f in g_main_dispatch (context=0x555556c62850) at ../glib/gmain.c:3444
#15 g_main_context_dispatch (context=0x555556c62850) at ../glib/gmain.c:4162
#16 0x00007ffff7167dd8 in g_main_context_iterate (context=0x555556c62850, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:4238
#17 0x00007ffff71680c3 in g_main_loop_run (loop=0x555556f0fc40) at ../glib/gmain.c:4438
#18 0x00007ffff77fa31d in gtk_main () from /usr/lib/libgtk-3.so.0
#19 0x0000555555bdcc45 in wxGUIEventLoop::DoRun() ()
#20 0x0000555555d5e111 in wxEventLoopBase::Run() ()
#21 0x0000555555d27033 in wxAppConsoleBase::OnRun() ()
#22 0x0000555555db66b7 in wxEntry(int&, wchar_t**) ()
#23 0x0000555555824fde in main (argc=<optimized out>, argv=0x7fffffffe678) at ../source/main.cpp:183

I'm not sure yet how to efficiently debug Playdate game crashes. I played around by adding panics at various places until I saw rust_begin_unwind in the backtrace, but it seems that no matter where I intentionally panic, it always takes the place of the segfault.

However, by luck, I managed to find a couple of workarounds, and I'm hoping these will point to a culprit. First, if I clear graphics before calling randomize, everything works fine:

         if !self.started {
+            graphics.clear(LCDColor::Solid(LCDSolidColor::kColorWhite))?;
             randomize(&graphics, &mut self.rng)?;
             self.started = true;
         }

It also works fine if I draw something to the screen before randomization, which doesn't seem anything like a real solution, but may be interesting data:

     fn update(&mut self, _playdate: &mut Playdate) -> Result<(), Error> {
         let graphics = Graphics::get();
+        graphics.draw_text("let's panic", point2(50, 50))?;
         if !self.started {

So, I suspect that something is reading from or writing to uninitialized state in some way..? I'm not experienced in this area.

If I build with --device it works fine on my 1.12.3 Playdate, oddly enough. [edit: also works on device after updating to 1.13.1.]

So it actually works if I set the bindings generation script to generate a linux-specific binding and then I use that instead of the playdate binding file (which it defaults to when not on macOS)

I wonder if it's related to that or if that just hides some undefined behavior somewhere

Also this example definitely used to work in the simulator on Linux a while ago

rtsuk commented

It's surprising that it worked at all with the arm32 bindings on x64. This is definitely the right way to approach it.