Debugging with Dynamic Printf Breakpoints

I’m not a big fan of using printf() in embedded applications, but I have to admit that in some cases it is very useful. One problem in debugging embedded systems debugging is getting values or information off the target: because of the limited resources this can be very challenging.

So why not doing this with the debugger in an automated way? And here dynamic printf breakpoints can help: it adds printf()-style output on-the-fly to your program without the need to recompile or restart your program, without the need to run printf() on the target:

Using Dynamic Printf Breakpoint

Outline

The feature is the ability for the debugger or debug session to use the application printf() to communicate to the outside world. Instead that the code calls directly the printf() function, it is the debugger which can do this.

One way to output information in printf() style to the host is using semihosting (see Silicon Shortage and Semihosting with NXP MCUXpresso SDK on FRDM-KE02Z), however this requires the printf() code executed on the target with debugger intervention which is very intrusive.

So why not use the debugger to get the information off the target, but have the printf() itself exected by the debugger on the host? Here is where the Eclipse CDT Dynamic Printf Breakpoint feature comes into play.

There is a feature in Visual Studio Code named logpoints. The Eclipse dynamic printf is a feature of Eclipse CDT 8.4. Note that the feature recent gdb version of 7.7 or later. In this article I’m using the NXP MCUXpresso IDE 11.5.0 which supports dynamic printf breakpoints. What it does is when hitting a dynamic breakpoint, it inserts on-the-fly a call the printf() with the given arguments.

Adding Dynamic Printf Breakpoint

The breakpoint can be added as usual in the disassembly or source view, like any other breakpoints:

If adding several breakpoints, I can change the default type of breakpoint to be added:

By default it adds a breakpoint printing the source line:

That default can be changed to anything printf() would accept. So for example I can print the value of a variable:

One thing to keep in mind is that the output goes to the Debugger Console view:

dprintf

The gdb used feature for dynamic printf is the dprintf command. You can see this if you do a ‘info break’:

The behavior of it is highly configurable, and by default it uses the printf() on the host (gdb). Usint the ‘set dprintf-style’ it is possible to change this to the printf() code on the target or to any other function you specify. To find out what is getting called, check ‘dprintf-function’ and ‘dprintf-style’: Below it shows that it is using the printf() of gdb.

With ‘set dprintf-style call’ it will call e.g. the printf() application code. In that case, make sure you have it present (linked) in your application, e.g. through using semihosting.

Considerations

Keep in mind that that this feature is still rather intrusive compared to natively printing things over an UART or similar channel. But because it uses the printf() in the debugger and on the host, I don’t have to add the printf code to my application, and it is only used with the debugger: So this is fine and especially useful for ‘sporadic’ printf’s. Do not use dynamic printf breakpoints in ‘tight’ loops or too frequently, as this can add a lot of overhead and making the target/debugger unresponsive.

The other thing is that of course the debugger needs to ‘see’ the variables/symbols in the active context where the breakpoint is installed. If that variable is not visible or does not exist, you will get an error message:

Summary

Dynamic printf comes handy if I need to print out values from the target or any other kind of information, without having printf itself present on the target as it gets executed on the host if using dprintf-style with gdb. It is still intrusive as the debugger halts the target, prints the output and resumes it. If not overusing that feature, it is very useful and versatile feature for debugging an embedded application. The gdb dprintf functionality makes it very configurable and versatile, so this is one more hidden gem inside the GNU debugger and Eclipse.

Happy printing 🙂

18 thoughts on “Debugging with Dynamic Printf Breakpoints

    • The difference is that with using RTT the application is running the ‘print’ code and the RTT is doing the transfer, similar to semihosting. So you actually need the ‘writing’ code be present on the target. While with gdb dprintf this is not needed, so all what it is is a breakpoint and then things are handled by the host, and you don’t have to touch/recompile the target. From a performance standpoint you will be faster just writing the information/value directly (UART, RTT, …) because with gdb it will stop the target, get the content/memory through gdb/mi and print it on the host. So if you are going to dump lots of information: go with RTT or UART or whatever. My recommendation is: I you need to dump ‘sporadic’ information and want to use printf-style without changing or adding things to the target, use dprintf. And you can direct dprintf to use RTT too, if you would like to combine the two concepts 🙂

      Like

      • Yeah I understand what its doing. My question was more to do with what advantages and/or when would I use this? and the answer I guess is never 🙂 I mean I just cant think of a use case outside of a ‘sporadic’ need as you say. Might be useful for a technician performing some kind of hack/diagnostics maybe… but I just cant see a use for it.

        finally, to all those that seem to object to printf’s in their code… why? I use printf’s in my logging interface which is attached nearly all of my objects. From there I can route the data to RTT, UART and even my Tracealyzer buffer (similar to Python).

        But it is a neat trick and good to know. Just in case I have a ‘sporadic’ need! 🙂

        Liked by 1 person

        • I use the dynamic printf for sporadic things while debugging. It comes in handy if I need to run the target and keep things monitoring.
          About using printf() in the code: I avoid them because of possible buffer overflows and because they are rather huge, because they are supporting lots of formatting probably never used in the application. I had written an article on that subject here: https://mcuoneclipse.com/2013/04/19/why-i-dont-like-printf/ . I agree it is handy especially in some cases (if you have enough resources on the target). If I need something along the printf() style, then I rather use a version more suitable for embedded, e.g https://mcuoneclipse.com/2014/08/17/xformat-a-lightweight-printf-and-sprintf-alternative/. With the side note that this won’t support all the bells and whistles of the standard printf, but with a lot less footprint (ROM and RAM).

          Like

  1. Interesting, but as you say, only useful when you want “one time log”
    A nice trick to know 🙂

    I recently read about trice and I think I will it a try o my next project

    Liked by 1 person

    • Hi Kevin,
      Thank you! This feature has helped me in debugging hard problems many, many times. I just assumed it is a known debugging feature, but when I had a discussion with a peer recently, I have found out that this assumption was not true. So I wrote that article :-).

      Like

  2. The ability to add debug statements on-the-fly, through the application printf even,without needing to recompile, is really useful!
    I have just tested it on S32DS with S32K, works like a charm with semi-hosting enabled.
    One thing to be noted is that it adds malloc calls from the nano library before injecting the statement into our application printf (which itself does not uses malloc), so for us it created some MPU errors (access to __malloc_free_list) as some parts of the application did not have MPU access rights to the malloc bss data.
    Thanks for sharing Erich!

    Liked by 1 person

    • Interesting observation about using malloc(): I’m usually not using malloc() but the FreeRTOS heap, I’m wondering why I have not observed it (or simply missed it). Thanks for sharing too!

      Like

      • We use SafeRTOS which does not use the heap allocation schemes of FreeRTOS, but requires to allocate statically. Then we use an embedded printf form Marco Paland (https://github.com/mpaland/printf), which I discovered through comments on your post about xprintf. So my guess is that the malloc comes from the “instrumented code” that is added by the debugger for the dprintf function call that calls our printf (in normal dprintf-style gdb, the problem is not there, only for call style).

        Liked by 1 person

        • Yes, that indeed could be the case if it is using printf() on the target. It depends on the library used (I believe newlib-nano does not use malloc() for printf, while newlib does). So I think you are using newlib, right? So I think it is not a problem of gdb but of the printf library and implementation. But have you seen that you can redirect gdb to use your custom printf symbol?

          Like

        • We use newlib-nano, but not printf in our application. I think maybe my answer was not clear. The issue arises not when I use the printf to gdb, but when I do redirect to my application custom printf symbol, which itself does not use malloc.
          I do : “set dprintf-style call” then “set dprintf-function safe_printf” (our application printf based on that of Marco Paland).
          I wonder if when using the call style, the gdb needs to do some formatting before passing the arguments to the application function, and there uses the library.

          Liked by 1 person

        • ok, I see what you mean. I don’t understand why gdb would use the target malloc for this, as it could be done on the host too if it is purely for some formatting purposes. What I could imagine is that it uses malloc for some of the buffers needed for the communication, not sure.

          Liked by 1 person

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.