pybricks / support

Pybricks support and general discussion
MIT License
107 stars 6 forks source link

[Bug] using f'format strings in tight loop blocks and triggers watchdog timer #1668

Open BertLindeman opened 3 months ago

BertLindeman commented 3 months ago

Describe the bug Program is a tight loop printing f-strings. Within seconds the hub does a shutdown (somehow) The display animation is no longer smooth but has large hickups.

Might this be related to #436 ?

To reproduce Steps to reproduce the behavior:

Aditional info Connected the hub before the test to USB to keep the power on. After the crash connect to the hub and go to the REPL. Showed:

>>> hub.system.reset_reason()
2
>>> 

Expected behavior You'd expect the program to just keep running and not to power-off.

Screenshots There is a saying that a picture is worth a 1000 words. Screenshots really help to identify and solve problems.

test program for this issue ```python # issues try_movehub_on_prime_6 # start this and while running disconnect with the app # stop the hub to save the current program # restart the hub (if build 3380 installed press the BT button -> red) # start the program with the button, and # within seconds the running square stops and the hub shutsdown from pybricks.hubs import PrimeHub from pybricks.tools import wait HUB = PrimeHub() # Initialize variables. color_list = ["", "", "", "", "", "", ""] _rand = HUB.battery.current() + HUB.battery.voltage() # _rand = 123 INDEX = 4 A = 0 B = 5 def main2(): while True: print(f'pre _rand {_rand:>5} A {A:>2} B {B:>2}', end=" ") print(f'_rand {_rand:>5} A {A:>2} B {B:>2}') print(f'INDEX {INDEX:>3} color_list[{INDEX}] {color_list[INDEX]}') # wait(1) # with 1ms second wait: no problem wait(0.75) # this wait is not enough to prevent the error main2() ```
laurensvalk commented 3 months ago

Good find! When not connected, it is supposed to ignore printed output but that clearly isn't happening.

laurensvalk commented 3 months ago

Did you try this on any other hub?

BertLindeman commented 3 months ago

Did you try this on any other hub?

Up to now only the Spike and Inventor. Will "do" others.

laurensvalk commented 3 months ago

More minimal program and method below.

Run this on SPIKE Prime and press the disconnect button in Pybricks Code. No need to restart anything.

while True:
    print(f'hello - hello - hello - hello - hello - hello - hello')
    print(f'hello - hello - hello - hello - hello - hello - hello')

The hub doesn't quite crash all the way, but the animation still visibly stutters. Just for less than it needs for the watchdog timer to trigger.

laurensvalk commented 3 months ago

Yet this is OK. So I guess we'll have to have a look at the format-string source code:

while True:
    print('hello - hello - hello - hello - hello - hello - hello')
    print('hello - hello - hello - hello - hello - hello - hello')
BertLindeman commented 3 months ago

This one stutters:


while True:
    print(f'{"hello":>10} -{"hello":>10} {"hello":>10} {"hello":>10} {"hello":>10}')
    print(f'{"hello":>10} -{"hello":>10} {"hello":>10} {"hello":>10} {"hello":>10}')

    print(f'{"hello":>10} -{"hello":>10} {"hello":>10} {"hello":>10} {"hello":>10}')
    print(f'{"hello":>10} -{"hello":>10} {"hello":>10} {"hello":>10} {"hello":>10}')
laurensvalk commented 3 months ago

Yes, any long f'string' seems to be an issue. Normal strings seem fine.

BertLindeman commented 3 months ago

Did you try this on any other hub?

How to "see" the stutter the other hubs? I have the Move / City / 2 Technic hubs running with 8 of these print lines. The inventor and prime hub stop automagically but the others run happily on. Maybe the blue light is a bit more step by step. But now very clear.

laurensvalk commented 3 months ago

Thanks for having a look! Since it seems to be specifically related to these format strings, I think it's OK to stick to just the primehub for now to keep it simple.

Thanks for bringing it to our attention. I haven't used these f-strings much in Pybricks for some reason.

BertLindeman commented 3 months ago

Thanks for having a look! Since it seems to be specifically related to these format strings, I think it's OK to stick to just the primehub for now to keep it simple.

Thanks for bringing it to our attention. I haven't used these f-strings much in Pybricks for some reason.

I like the f-strings more and more. Just getting to know the formatting characters.

BTW this was the issue I found while testing the BT-on/off change in build 3380.

BertLindeman commented 3 months ago

You know me, I keep fiddling 😄

Idea was does the f-string pose the problem or the print doing the formatting.

This snippet:


formatted1 = f'{"hello" * 3:>20} - {"wow"[:2]:>10} {"hello":>10} {"hello":>10} {"hello":>10}'
print(type(formatted1))
formatted2 = f'{formatted1[:10]:>20}'

while True:
    formatted2 = f'{formatted1[:10]:>20}'
    print(type(formatted1), formatted1)
    print(formatted2)
    print(type(formatted1), formatted1)

with an f-string in the loop makes the animation stutter.

The same program but the formatting commented out within the loop does not stutter:


formatted1 = f'{"hello" * 3:>20} - {"wow"[:2]:>10} {"hello":>10} {"hello":>10} {"hello":>10}'
print(type(formatted1))
formatted2 = f'{formatted1[:10]:>20}'

while True:
    # formatted2 = f'{formatted1[:10]:>20}'
    print(type(formatted1), formatted1)
    print(formatted2)
    print(type(formatted1), formatted1)

The watchdog is not triggered.

The 3 print commands are needed to see the stutter.

[EDIT] Without the print statements in the loop, but with f-strings "that do formatting on slices" the running display animation is skipping frames, in my terms it stutters. With SLICE = False the normal display animation.

The boolean SLICE determines which loop runs: f-strings with or without slices ```python # SLICE = False # normal animation SLICE = True # animation skips frames; my term: it stutters if SLICE: while True: # loop with only f-string formatting and slices formatted1 = f'{"hello?"[:-1] * 3:>20} - {"wow"[:2]:>10} {"hello"[:4]:>10} {"hello":>10} {"hello":>10}' formatted2 = f'{formatted1[6:]:<20}' else: while True: # loop with only f-string formatting and NO slices formatted1 = f'{"hello" * 3:>20} - {"wow":>10} {"hello":>10} {"hello":>10} {"hello":>10}' formatted2 = f'{formatted1:<20}' ```
Movies: https://github.com/pybricks/support/assets/8142081/172d6c90-fa77-40ec-ae70-7bf38d993b76 https://github.com/pybricks/support/assets/8142081/7050dcab-dc68-4c92-8954-c9e353668c8e https://github.com/pybricks/support/assets/8142081/45b10344-0fb8-40f2-b6b8-bb373b06314f https://github.com/pybricks/support/assets/8142081/ecbff100-9d6c-4b11-bda9-15f21e8ff7e6