Debuging the micropython core

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
Post Reply
volkerjaenisch
Posts: 5
Joined: Thu Jun 10, 2021 9:38 pm

Debuging the micropython core

Post by volkerjaenisch » Wed Sep 08, 2021 11:11 pm

Dear MicroPython People!

I am currently preparing a MicroPython Core enhancement to framebuf https://github.com/micropython/micropython/issues/7750 .

But I am new to the micropython core and may be have missed some briefings :-).

I have a complete esp32 cross-compile platform running for my controller. So changes on my C-Core-Code are well reflected in the behavior of my esp32 controller. My new core functions for framebuf are working but not as intended.

So how can I debug these functions? Is their any IDE that supports debugging Micropython?

If not:
How can I print a simple message to the python console from my C-Core-code?

Any help appreciated

Volker

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

Re: Debuging the micropython core

Post by stijn » Thu Sep 09, 2021 6:10 am

I'll let others answer the 'how to debug C code on an ESP32' aspect, but here's an other idea: framebuf is mostly platform agnostic anyway, so provide an implementation which works for the unix port (either a dummy one which just provides a piece of memory for the pixels, or an actual one; it used to be possible to get a framebuf i.e. raw pointer to display memory on standard linux distros) and you can use any debugger you like.

User avatar
russ_h
Posts: 88
Joined: Thu Oct 03, 2019 2:26 am
Contact:

Re: Debuging the micropython core

Post by russ_h » Thu Sep 09, 2021 6:33 am

You can printf() from c code and it will printed to the REPL console. Another thing that can help is to add a monitor target in ports/esp32/Makefile.

Code: Select all

deploy:
        idf.py $(IDFPY_FLAGS) -p $(PORT) -b $(BAUD) flash

monitor:
        idf.py $(IDFPY_FLAGS) -p $(PORT) -b $(BAUD) monitor

erase:
        idf.py $(IDFPY_FLAGS) -p $(PORT) -b $(BAUD) erase_flash
This will let you connect to the REPL over the usb com port and if your code crashes it will automatically print a stack trace for the code that was running. If you compile with DEBUG=1 the trace will show source code line numbers as well.

Code: Select all

make monitor
I haven't found a way to compile user c modules with line number information. I do not know CMake very well, so it could be something I'm not doing correctly.

User avatar
reanimator
Posts: 2
Joined: Fri Sep 10, 2021 6:32 pm

Debuging the micropython core / ITM trace

Post by reanimator » Fri Sep 10, 2021 6:54 pm

Hi there,

I would like to ask a question relevant to mp-core debugging topic too. I ask the question here since exactly the thread has been already created now.

Currently I am working on porting the circuitpython on NXP LPC series MCUs.
In order to debug memory allocation issues (it turned to be my fault) I added ITM-trace support and implemented DEBUG_printf to redirect debug information to SWO channel. It worked out quite well except one piece of code in mp_builtin___import__.

Code: Select all

mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
    #if DEBUG_PRINT
    DEBUG_printf("__import__:\n");
    for (size_t i = 0; i < n_args; i++) {
        DEBUG_printf("  ");
        mp_obj_print(args[i], PRINT_REPR);
        DEBUG_printf("\n");
    }
    #endif
The problem is that the mp_obj_print still uses stdout (i.e. REPL-console).
Is there any easy, clean way to elaborate the code (e.g. of mp_obj_print function?) to inject the custom printer support?
What about MP_PYTHON_PRINTER?

Here is the call stack to have complete picture:
callstack.png
callstack.png (250.36 KiB) Viewed 5707 times

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

Re: Debuging the micropython core

Post by stijn » Sun Sep 12, 2021 3:48 pm

I think that's an oversight, instead of mp_obj_print that should be mp_obj_print_helper(MICROPY_DEBUG_PRINTER or similar
If you'd change the code anyway you could create a pull request for it?

User avatar
reanimator
Posts: 2
Joined: Fri Sep 10, 2021 6:32 pm

Debuging the micropython core

Post by reanimator » Sun Sep 19, 2021 10:52 am

stijn wrote:
Sun Sep 12, 2021 3:48 pm
I think that's an oversight, instead of mp_obj_print that should be mp_obj_print_helper(MICROPY_DEBUG_PRINTER or similar
If you'd change the code anyway you could create a pull request for it?
Thanks for you reply!

Sounds reasonable, but I am not completely sure how to proceed.
The new printer MICROPY_DEBUG_PRINTER is going to another be mp_stream_adaptor, right?

mp_print_strn function calls print_strn functor of new printer of mp_print_t type:

Code: Select all

    if (len) {
        print->print_strn(print->data, str, len);
        total_chars_printed += len;
    }
print_strn functor is actually mp_stream_write_adaptor function:

Code: Select all

void mp_stream_write_adaptor(void *self, const char *buf, size_t len) {
    mp_stream_write(MP_OBJ_FROM_PTR(self), buf, len, MP_STREAM_RW_WRITE);
}
So mp_stream_write_adaptor may pass new flag to mp_stream_write, like follows:

Code: Select all

void mp_stream_write_adaptor(void *self, const char *buf, size_t len) {
    mp_stream_write(MP_OBJ_FROM_PTR(self), buf, len, (MP_STREAM_RW_WRITE | MP_STREAM_DEBUG));
}
then mp_stream_write passes these flags to mp_stream_rw

Code: Select all

mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode, byte flags) {
    byte *buf = buf_;
    typedef mp_uint_t (*io_func_t)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
    io_func_t io_func;
    const mp_stream_p_t *stream_p = mp_get_stream(stream);
    if (flags & MP_STREAM_RW_WRITE) {
        io_func = (io_func_t)stream_p->write;
    } else {
        io_func = stream_p->read;
    }
Here the I/O functor (debug stream) could be easily selected:

Code: Select all

mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode, byte flags) {
    byte *buf = buf_;
    typedef mp_uint_t (*io_func_t)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
    io_func_t io_func;
    const mp_stream_p_t *stream_p = mp_get_stream(stream);
    if (flags & MP_STREAM_RW_WRITE) {
        if (flags & MP_STREAM_DEBUG) {
            io_func = (io_func_t)stream_p->dbg_write;
        } else {
            io_func = (io_func_t)stream_p->write;
        }
    } else {
        io_func = stream_p->read;
    }

In this case mp_stream_p_t has to be modified. Isn't it too much code to alter?

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

Re: Debuging the micropython core

Post by stijn » Tue Sep 21, 2021 9:49 am

I'm not really following - have a look at how other ports implement MICROPY_DEBUG_PRINTER: you don't have to modify how printing with it works, MICROPY_DEBUG_PRINTER is only used for debug printing anyway. Also see shared/libc/printf.c: you don't have to write your own DEBUG_printf, instead you only have to provide an mp_print_t object which prints to whatever you want, in this case that SWO channel. So you only need something like this:

Code: Select all

//somewhere in a .c file
STATIC void swo_print_strn(void *env, const char *str, size_t len) {
    (void)env;
    //print str to SWO here
}

const mp_print_t mp_swo_print = {NULL, swo_print_strn};

//and in mpconfigoprt:
#define MICROPY_DEBUG_PRINTER       (&mp_swo_print) 

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

Re: Debuging the micropython core

Post by stijn » Tue Sep 21, 2021 10:45 am

See https://github.com/micropython/micropython/pull/7834
Wit this e.g. the unix port will properly print the arguments etc to mp_stderr_print (which is what it uses by default for MICROPY_DEBUG_PRINTER)

Post Reply