File open crashes on device C API

Hi, I'm having issues opening file handles on device using this function:

SDFile* playdate->file->open(const char* path, FileOptions mode);

In simulator it just works but on device it just crashes for me. I can see that in the simulator a folder is created for the files in the data section but on the device I have no such application folder. I tried creating one manually but it changed nothing.

I've not found anything in the documentation but have I forgotten some simple settings or compiler flag maybe to allow saving and opening files?

Cheers

ERik

Ok, been doing a bit of narrowing down. Running it from the init event seems ok in:

int eventHandler(PlaydateAPI* pd, PDSystemEvent event, uint32_t arg)

But in the update loop it always crashes:

static int update(void* userdata)

Cheers

ERIk

It's hard to tell without seeing how you are using it in code. One thing to note is that the game's data directory will be different depending on if you sideload through the website or through USB. That shouldn't really affect how you use the file->open function though.

This is how I use it:

SDFile *file = playdate->file->open("test.json", kFileWrite);

It crashes on this line. Sideloading it from the website and the simulator seems to make no difference for me.

I should maybe add that also mkdir crashes for me, similarly on device but not in simulator:

int error = playdate->file->mkdir("test");

Cheers

ERik

Is it crashing exactly on those lines, or does it at least return some kind of error after trying to open the file? You could try printing playdate->file->geterr() right after using playdate->file->open and see if it gives some kind of info. Also I would make sure you are using the latest version of the SDK and have your device updated to the latest firmware (1.12.3).

No, no error or return on the function because it crashes on that line. I am using the latest firmware. After some more testing it seems to be related to running the microphone input using this function:

void playdate->sound->setMicCallback(AudioInputFunction* callback, void* context, int forceInternal)

I think my results before running it successfully from init was because that ran before starting mic input. Hmm, that's disappointing results. :frowning:

Cheers

ERik

Hm, so it only happens after calling setMicCallback? That sounds like a strange bug. If you could write some simple example code that reproduces the bug on device, maybe @dave could look into it.

1 Like

Yes only after calling setMicCallback, my application runs the mic continuously too, so I would prefer not switching it off and on while accessing files. Will try to create a project with just the bare minimum to recreate the issue.

Cheers for the input!

ERik

1 Like

Here is where I am at the moment. I tried yesterday evening to build a small example showing the crash, but the minimal example of microphone input + file open didn't result in a crash there, so that must mean it is something else causing it indirectly by the mic callback. But I am not sure yet.

My gdb skills are less than good I think. Trying to run the elf file and printing the address lines from the crash log always seem to print "No line number information available for address..". I am probably doing something wrong there. This is what the crash log looks like:

--- crash at 2023/01/04 19:06:41---
build:59185ded27c7-1.12.3-release.140884-buildbot
   r0:00000024    r1:080462f5     r2:20010220    r3: 00000000
  r12:0000000a    lr:080462f5     pc:0801e9c6   psr: 21000000
 cfsr:00000000  hfsr:40000000  mmfar:00000000  bfar: 00000000
rcccsr:00000000
heap allocated: 2239776
Lua totalbytes=0 GCdebt=0 GCestimate=0 stacksize=0

I don't know if you guys can spot something obvious without having the elf file?! Probably not.

Cheers! More effort required I guess..

ERik

The memory map looks roughly like

0x08000000 - 0x08100000: flash memory (firmware storage)
0x20000000 - 0x20050000: system memory
0x60000000 - 0x61000000: game heap

and your pdex.bin game code is loaded to the start of the system heap, so you can tell at a glance if a crash address is in your code or in the firmware. Here it's at 0x0801e9c6 called from 0x080462f5 (if we can trust the trace data) which are both in kernel code, so they're not even in the bin/symbols.db file in the SDK that the simulator's sampler uses to symbolize traces. The $pc address is in the filesystem's write function and $lr is in FreeRTOS, which makes sense, but the CFSR fault status register is empty, which.. doesn't. That suggests that the data here isn't very reliable.

If you've got a crashing pdx you can send me I'd be happy to run it on the hardware debugger, see if I can figure out what's going on!

4 Likes

Thanks for the help, I sent an email with the pdx and instructions of how to crash it.

Cheers

ERik

Wow, this is a weird one! First thing, it's crashing because the run loop is hanging. It should be putting Run loop stalled for more than 10 seconds pc=0x600023E8 lr=0x08084EDF on the screen, but because the hang is in the filesystem code and it's trying to write that message to disk before it draws it to screen, it never gets that far. I'll fix that so it updates the screen first.

So, next, it looks like 0x600023E8 is in the audio code (I see a bunch of floating point ops in that area) which would be running on the audio task instead of the game task, so it isn't where the actual hang is, it's just what code was active when the runloop watchdog timer fired. So I'll see if I can have that error message show the location of the game task instead. Though in this case you'd get a location down in kernel code which wouldn't really tell you much because those ranges aren't in symbols.db. :confused:

It looks like this is some FreeRTOS task priority issue that's way beyond my comprehension. The audio task runs at a higher priority than the game task since it needs to get data to the output as fast as possible to avoid cracking audio. It looks like that's somehow dragging the filesystem code to a crawl even though it's only using around 60-70% of the CPU, according to the device stats view. I couldn't figure out where in the filesystem it's yielding its CPU time (or whatever it's doing) but when I set the audio task priority to the same as the game task's, the problem goes away. I assumed this would lead to a bunch of audio problems in other games, but Saturday Edition and Inventory Hero sound fine. Maybe even better. I'll have to do more testing, but maybe that's the best solution here.

So what can we do to get your code working now, since that change wouldn't ship for a while? Probably the easiest solution is to pause the audio processing while you're saving and loading the settings. (Adding a fade out and fade in would make this less jarring.) There might be other places in the system, though, where running the audio task that hard causes surprise problems like this. The "correct" solution is to move the audio processing to the game task's update callback, feeding the data to a ring buffer that the audio render callback gets its data from. In general, audio callbacks should return as quickly as possible.

But maybe that priority change will do the trick? :man_shrugging:

1 Like

...because the hang is in the filesystem code and it's trying to write that message to disk before it draws it to screen, it never gets that far. I'll fix that so it updates the screen first.

Oh right, we now draw the falling blocks error screen instead of the message. So my "fix" makes it show that screen and then it hangs and crashes, which is even worse. :frowning:

Cheers I really appreciate it. Those floating point ops definitely sound like my audio code. I think I'll go for a fade in and out for now. Or limit the number of possible presets and load it all on start and save on terminate. Do you think there is a bit of time to open and write to file on terminate or will it kill the game instantly?

Feels risky to tinker with the audio thread priority. Please don't mess with other peoples games because of my crazy experiments. :sweat_smile:

Many thanks!

ERik

1 Like

I spent the weekend porting the formant synth and reverb effect from Vocoder Puccini | Dittytoy and immediately ran into the same problem. I've asked Marc to take a look at whether the priority change is a safe change or not--he's a lot smarter about this stuff than I am. :slight_smile:

2 Likes

Update on this: I found a better solution than changing the task priority. Turns out the main reason we're hanging is that the codec driver will request another frame from the audio task when it's still busy with the previous instead of just dropping it and flagging an underrun. If I have it drop the frame there's no hang, and loading down the game task by doing a bunch of drawing causes the display frame rate to drop without affecting audio performance like it does when I lower the audio priority. The only thing I notice in your demo is a small click when I save the settings.

I still don't know why the audio task wasn't able to catch up on its render backlog, considering the reverb unit in my test only takes half the CPU during normal usage. I'll look into that later when I have time; I suspect it might be related to your microphone problem. Seems like there's a bit of weirdness in how we're handling buffers and callbacks and task queues and all that.

2 Likes

Excellent good work! Sounds like the right approach to not change priorities. I’ll probably continue doing a fade in and out then to avoid any clicks. :slight_smile:

Cheers

ERik

1 Like

For the record, in 2.3 we're now skipping audio frames when the audio task can't keep up instead of letting it drag down the system. Let me know if you notice any odd side effects from this!

Cheers that is good to know, will test it out tonight. Makes sense to skip frames I think.

Erik