Assert error during garbage collection [Solved]

Discussion and questions about boards that can run MicroPython but don't have a dedicated forum.
Target audience: Everyone interested in running MicroPython on other hardware.
User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Assert error during garbage collection [Solved]

Post by Roberthh » Wed Apr 15, 2020 3:24 pm

In my attempt to deal with the w600 port, I get sometimes an assert error, which happens during memory operations, just when the device is about or when it is to perform a garbage collection. The error message is:

Code: Select all

assertion "ATB_GET_KIND(block) == AT_HEAD" failed: file "../../py/gc.c", line 590, function: gc_free
The obvious message is, that the memory is somehow corrupted. But:
I happens when I call a short test script, let's call it test(), which save a few lines of text to a file. It ONLY happens when I execute this script's function by calling it from the REPL prompt manually by entering test() over and over. If I call this script function in a short loop, e.g. with "for _ in range(200): test()", everything runs well.
Has anyone a hint about what is memorywise different between calling from REPL and calling in a short loop?

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Assert error during garbage collection

Post by dhylands » Wed Apr 15, 2020 6:25 pm

The REPL has a history buffer where it stores some number of previous commands.

Actually, there are 2 history buffers. One is just the previous line MP_STATE_VM(repl_line).

And then readline has a history buffer called readline_hist. Using readline_hist requires a call to readline_init0. If you don't use readline, then you need to call pyexec_event_repl_init.

So I don't know if that's your problem, but it is one difference between executing from a script and from the REPL.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Assert error during garbage collection

Post by Roberthh » Wed Apr 15, 2020 7:39 pm

Thanks, Dave. What puzzles me is, that REPL works fine, and there is little extra stuuf in that port with respect to REPL. Just the char in and out on the UART using a static buffer. No relation in any kind to the memory management and GC.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Assert error during garbage collection

Post by Roberthh » Wed Apr 15, 2020 8:11 pm

Another observation: That happens only during automatic garbage collection, when going close to 0 or to whatever threshold is set. If I add in my python test script a gc.collect() when the memory is below a certain limit, all works well.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Assert error during garbage collection

Post by dhylands » Wed Apr 15, 2020 9:12 pm

The heap should be in memory after all of the data and bss (worth double checking).

I'd also look closely at the stack. It normally comes out of high memory and grows towards the heap. If the stack was overflowing, it would corrupt the heap.

A common technique I use is to put a guard at the end of the stack which contains all 0x55's or some other value like that. I like to make the guard a bit largish (like 256 bytes). Then you can check the integrity periodically from within the code. If the guard is too small, then if a C code declares a stack buffer larger than your guard it can straddle the guard (still corrupting the heap) and not be detected.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Assert error during garbage collection

Post by Roberthh » Thu Apr 16, 2020 6:11 am

Thanks. I'll check. In the case here the stack for the main task is statically allocated and at a lower address than the heap. And, as far as I could tell, stack check is enabled. But I can add the test pattern to check for changes, or increase it.

Edit: Thank you for pointing my attention to the stack setting. There was indeed an error. The documentation about the task start call of the OS seems to be wrong. While it says, that the stack size is to be given in stack units, which is an uint32 here, it actually treats the give size as bytes. So the stack pointer for the task was initialized wrong. But it did not solve the initial problem, sigh!

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Assert error during garbage collection

Post by Roberthh » Thu Apr 16, 2020 7:41 pm

Looking at the difference between REPL repeat and running in-line, there is obviously a parse_compile_execute() call in every repetition. But that usually runs well and I assume it contains nothing port specific.

stijn
Posts: 735
Joined: Thu Apr 24, 2014 9:13 am

Re: Assert error during garbage collection

Post by stijn » Thu Apr 16, 2020 8:07 pm

Reminds me somewhat of https://github.com/micropython/micropython/issues/4652 which has a bunch of interesting information in the comments on what you could check (a bit of a read though).

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Assert error during garbage collection

Post by Roberthh » Thu Apr 16, 2020 8:41 pm

Hello @stijn, thanks for the link. I contains indeed a lot of useful information. As far as I understood, this problem had been solved about a year ago, and I am using the most recent state of the repository. But the approach for the search is very useful.

stijn
Posts: 735
Joined: Thu Apr 24, 2014 9:13 am

Re: Assert error during garbage collection

Post by stijn » Fri Apr 17, 2020 8:01 am

Yes it was solved (contrary to the search for the fix, the fix itself was super simple, just forcing a function to never inline), but it only affected unix/windows ports. So I assume the root cause of what you're seeing here will be in the w600-specific gccollect.c. Maybe also something with inilining causeing a different stack layout than what microPython assumes or maybe some root pointer not being marked.

Post Reply