Skip to content

pkg/mplaland-printf: Use buffered output#21805

Merged
maribu merged 1 commit intoRIOT-OS:masterfrom
maribu:pkg/mpaland-printf/buffered-output
Oct 24, 2025
Merged

pkg/mplaland-printf: Use buffered output#21805
maribu merged 1 commit intoRIOT-OS:masterfrom
maribu:pkg/mpaland-printf/buffered-output

Conversation

@maribu
Copy link
Copy Markdown
Member

@maribu maribu commented Oct 19, 2025

Contribution description

With this patch a small (64 byte) on-stack is buffer is used. On stdio transports with extremely high overhead per call of stdio_write() (like e.g. telnet or semihosting), this will make the stdio usable again.

Compared to e.g. the stdio from newlib, the increased stack requirements are still modest.

Testing procedure

Run e.g. an app with lots of stdio output with an stdio transport that has a lot of overhead per stdio_write() call. E.g.

USEMODULE=stdio_semihosting make BOAD=<board_with_many_timers> -C tests/periph/timer RIOT_TERMINAL=semihosting flash term

In master, this will be unusable. (About two chars per second on for semihosting on master). With this PR, this should be faster.

Issues/PRs references

Fixes a regression from #21438

@maribu maribu added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Oct 19, 2025
@github-actions github-actions bot added the Area: pkg Area: External package ports label Oct 19, 2025
@maribu
Copy link
Copy Markdown
Member Author

maribu commented Oct 19, 2025

Ah, I also sneaked in a fix where stdio_write() would return a number smaller than the requested output. mpaland-printf should now loop until the transport actually managed to write all output.

@riot-ci
Copy link
Copy Markdown

riot-ci commented Oct 19, 2025

Murdock results

✔️ PASSED

30a4647 pkg/mplaland-printf: Use buffered output

Success Failures Total Runtime
10552 0 10552 09m:41s

Artifacts

@maribu maribu force-pushed the pkg/mpaland-printf/buffered-output branch from 646a6ff to 46359e3 Compare October 19, 2025 15:22
@crasbe crasbe added the Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors) label Oct 19, 2025
@Teufelchen1
Copy link
Copy Markdown
Contributor

Great thanks. I will need two or three days to find time to test this on hardware.

@benpicco
Copy link
Copy Markdown
Contributor

I suggest we s/picolibc_stdout_buffered/libc_stdout_buffered/g so you can only enable the feature when needed.

@maribu
Copy link
Copy Markdown
Member Author

maribu commented Oct 20, 2025

True.

With the number of patches stacking up and upstream being dead, I wonder if we should also just reduce our pain with the integration by just vendoring in the code.

@crasbe
Copy link
Copy Markdown
Contributor

crasbe commented Oct 20, 2025

With the number of patches stacking up and upstream being dead, I wonder if we should also just reduce our pain with the integration by just vendoring in the code.

Apparently there is an actively maintained fork:
mpaland/printf#128
https://github.com/eyalroz/printf

@maribu
Copy link
Copy Markdown
Member Author

maribu commented Oct 20, 2025

OK, but that fork is almost twice as large. And the addition of cmakes increases integration complexity and build time for little to no benefit in our use case...

Copy link
Copy Markdown
Contributor

@Teufelchen1 Teufelchen1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works great!

@Teufelchen1 Teufelchen1 added this pull request to the merge queue Oct 24, 2025
Copy link
Copy Markdown
Contributor

@mguetschow mguetschow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I think it wouldn't harm to add some documentation to the file about why we use the buffering?

@maribu maribu force-pushed the pkg/mpaland-printf/buffered-output branch from 46359e3 to 7ec55c4 Compare October 24, 2025 08:23
@crasbe crasbe removed this pull request from the merge queue due to a manual request Oct 24, 2025
@crasbe
Copy link
Copy Markdown
Contributor

crasbe commented Oct 24, 2025

I think you have to force-push again, Github seems to be confused because it was already on the queue 🤔

With this patch a small (64 byte) on-stack is buffer is used. On stdio
transports with extremely high overhead per call of `stdio_write()`
(like e.g. telnet or semihosting), this will make the stdio usable again.

Compared to e.g. the stdio from newlib, the increased stack requirements
are still modest.

In addition, the RIOT integration now also checks the return value of
`stdio_write()` and loops until all output has been written, fixing
loss of output if a transport does not write all data in one go.
@maribu maribu force-pushed the pkg/mpaland-printf/buffered-output branch from 7ec55c4 to 30a4647 Compare October 24, 2025 09:46
@crasbe crasbe added this pull request to the merge queue Oct 24, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 24, 2025
@maribu maribu added this pull request to the merge queue Oct 24, 2025
Merged via the queue into RIOT-OS:master with commit 380dc68 Oct 24, 2025
25 checks passed
@benpicco
Copy link
Copy Markdown
Contributor

benpicco commented Oct 29, 2025

Something is not quite right. When I run tests/sys/usbus_cdc_acm_stdio on nrf52840dk, I get

> help
2025-10-29 16:55:45,877 # ain():Tisis Rmhelp
2025-10-29 16:55:45,877 # shell: command not found: main():Tisis
> text 128
2025-10-29 16:55:49,680 # text 128
2025-10-29 16:55:49,680 # 
2025-10-29 16:55:49,680 # 
> help
2025-10-29 16:55:53,501 # 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567help

On 2025.01-branch this would produce

2025-10-29 16:57:31,235 # > text 128
2025-10-29 16:57:31,235 # 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2025-10-29 16:57:31,236 # 012345678901234567890123456789012345678901234567

@maribu maribu deleted the pkg/mpaland-printf/buffered-output branch October 29, 2025 16:35
@maribu
Copy link
Copy Markdown
Member Author

maribu commented Oct 29, 2025

Is that issue only happening on USB, or also on UART? If it depends on the transport, it is more likely triggering an issue in the transport rather than actually causing it.

There always has been something fishy with the USB CDC ACM, as the first command entered into the shell always contained part of the boot message printed.

@maribu
Copy link
Copy Markdown
Member Author

maribu commented Oct 29, 2025

Does it work up to 128 B?

/**
 * @brief Buffer size for STDIN and STDOUT data to and from USB when using
 *        the USBUS_CDC_ACM_STDIO module
 */
#ifdef CONFIG_USBUS_CDC_ACM_STDIO_BUF_SIZE_EXP
#define CONFIG_USBUS_CDC_ACM_STDIO_BUF_SIZE (1<<CONFIG_USBUS_CDC_ACM_STDIO_BUF_SIZE_EXP)
#endif
#ifndef CONFIG_USBUS_CDC_ACM_STDIO_BUF_SIZE
#define CONFIG_USBUS_CDC_ACM_STDIO_BUF_SIZE (128)
#endif

That seems to then trigger the "drop" flow control path in

    /* stuff as much data as possible into tsrb, discarding the oldest */
    old = irq_disable();
    n = tsrb_free(&cdcacm->tsrb);
    if (len > n) {
        n += tsrb_drop(&cdcacm->tsrb, len - n);
        buf += len - n;
    } else {
        n = len;
    }

If usbus_cdc_acm_submit() instead would just write what is possible (not discarding old data), mpaland_printf() would call stdio_write() in a loop for the remaining data until everything is written. Probably that is what needs to change.

@maribu
Copy link
Copy Markdown
Member Author

maribu commented Oct 29, 2025

Specifically, does this fix the issue?

diff --git a/sys/usb/usbus/cdc/acm/cdc_acm.c b/sys/usb/usbus/cdc/acm/cdc_acm.c
index 77c93bcb5a..5528d1f28b 100644
--- a/sys/usb/usbus/cdc/acm/cdc_acm.c
+++ b/sys/usb/usbus/cdc/acm/cdc_acm.c
@@ -158,19 +158,8 @@ size_t usbus_cdc_acm_submit(usbus_cdcacm_device_t *cdcacm, const uint8_t *buf, s
         irq_restore(old);
         return n;
     }
-    /* stuff as much data as possible into tsrb, discarding the oldest */
-    old = irq_disable();
-    n = tsrb_free(&cdcacm->tsrb);
-    if (len > n) {
-        n += tsrb_drop(&cdcacm->tsrb, len - n);
-        buf += len - n;
-    } else {
-        n = len;
-    }
-    tsrb_add(&cdcacm->tsrb, buf, n);
-    /* behave as if everything has been written correctly */
-    irq_restore(old);
-    return len;
+
+    return tsrb_add(&cdcacm->tsrb, buf, len);
 }
 
 void usbus_cdc_acm_set_coding_cb(usbus_cdcacm_device_t *cdcacm,
diff --git a/sys/usb/usbus/cdc/acm/cdc_acm_stdio.c b/sys/usb/usbus/cdc/acm/cdc_acm_stdio.c
index fe171e7420..aca489a16e 100644
--- a/sys/usb/usbus/cdc/acm/cdc_acm_stdio.c
+++ b/sys/usb/usbus/cdc/acm/cdc_acm_stdio.c
@@ -36,15 +36,9 @@ static uint8_t _cdc_tx_buf_mem[CONFIG_USBUS_CDC_ACM_STDIO_BUF_SIZE];
 
 static ssize_t _write(const void* buffer, size_t len)
 {
-    const char *start = buffer;
-    while (len) {
-        size_t n = usbus_cdc_acm_submit(&cdcacm, buffer, len);
-        usbus_cdc_acm_flush(&cdcacm);
-        /* Use tsrb and flush */
-        buffer = (char *)buffer + n;
-        len -= n;
-    }
-    return (char *)buffer - start;
+    size_t res = usbus_cdc_acm_submit(&cdcacm, buffer, len);
+    usbus_cdc_acm_flush(&cdcacm);
+    return (ssize_t)res;
 }
 
 static void _cdc_acm_rx_pipe(usbus_cdcacm_device_t *cdcacm,

@benpicco
Copy link
Copy Markdown
Contributor

Hm not really - what's pointing towards mpland is that the issue also occurs when I add USEMODULE += stdio_uart

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: pkg Area: External package ports CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants