Flush buffer created by printf retargeting

When I override the console, I see no way of flushing the buffer created by using printf.

For example, if I override the console with a BufferedSerial, printf statements without a line break in them are not flushed. They only are printed when printf is called with a line break.

Minimal reproducible code:

#include "mbed.h"

BufferedSerial serial(CONSOLE_TX, CONSOLE_RX, 115200);

FileHandle *mbed::mbed_override_console(int)
{
    return &serial;
}

int main()
{
    FileHandle *console_out = mbed_file_handle(STDOUT_FILENO);
    FILE *f = fdopen(console_out, "w");

    int counter = 0;

    while (1)
    {
        console_out->write("hi", 2); // written immediately
        serial.write("\tbye", 4); // written immediately
        fprintf(f, "\tf"); // written immediately
        printf("\t%d", ++counter); // not written until new line is printed

        fprintf(f, "\tg"); // printed _before_ counter

        serial.sync(); // does nothing
        console_out->sync(); // does nothing
        fflush(f); // does nothing

        ThisThread::sleep_for(1s);

        printf("\tFlushing now...\r\n");
    }
}

How do I immediately print when directly using printf (without getting a FILE * or FileHandle), but still use a BufferedSerial?

1 Like

Ah I solved it! For some reason, calling fflush on the FILE * from fdopen does not flush stdout. However, mbed properly defines stdout so that calling fflush(stdout) properly does flush the buffer from printf.

So the following code will flush immediately:

printf("Partial line");
fflush(stdout);

Writing an own printf implementation is an option, and probably the most recommended option according to me. Get some inspiration from the standard library implementation and write your own version, only to cater for your requirements. In general, what you have to do is, first retarget a put function to send char s through your serial interface. Then override the print method by using the put custom implementation. Perhaps, a very simple approach is sending the string character-wise by recursive calls for putc function.

Last but not least, you can find some lightweight printf implementations. The code size and the set of features offered by these lightweight implementations lie in between the custom written printf function and the stock standard prinf function (aka the beast). I have recently tried this and very pleased with its performance on an ARM core in terms of memory footprint.

Add this piece of code in your code

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE * f)
#endif

PUTCHAR_PROTOTYPE {
    HAL_UART_Transmit( & huart2, (uint8_t * ) & ch, 1, 0xFFFF);
    return ch;
}

Only thing to edit is this line, set it as per your device serial port driver

HAL_UART_Transmit( & huart2, (uint8_t * ) & ch, 1, 0xFFFF);

Hi,
I think this is because stdout is buffered by default.
Take below program as an example:
#include <stdio.h>

int main (void)
{
fprintf (stdout, “hello”);
fprintf (stderr, “world”);

return 0;

}

If I build and run it with below command lines:
arm-none-eabi-gcc -mthumb -mcpu=cortex-m3 -O2 test.c -o test.axf --specs=rdimon.specs -lc -lrdimon -lc . basically depending on this flush buffer I have developed erp for small businesses.
qemu-system-arm -nographic -serial null -monitor null -semihosting -kernel test.axf -cpu cortex-m3

The output is “worldhello” because stdout is buffered and stderr is not.

If I change the program into :
#include <stdio.h>

int main (void)
{
fprintf (stdout, “hello”);
fflush (stdout);
fprintf (stderr, “world”);

return 0;

}
With same command lines the output is “helloworld”.

I think this can illustrate the buffer strategy well.

Still I am not sure why fflush does not work in your case, which works fine for me.

As for the redlib, I think they might have the buffer facility optimized out, but this is just a guess without access to the source code.

Thanks.