Skip to content

Commit 3310382

Browse files
pabigotnashif
authored andcommitted
lib: add cbprintf capability
This commit adds a C99 stdio value formatter capability where generated text is emitted through a callback. This allows generation of arbitrarily long output without a buffer, functionality that is core to printk, logging, and other system and application needs. The formatter supports most C99 specifications, excluding: * %Lf long double conversion * wide character output Kconfig options allow disabling features like floating-point conversion if they are not necessary. By default most conversions are enabled. The original z_vprintk() implementation is adapted to meet the interface requirements of cbvprintf, and made available as an opt-in feature for space-constrained applications that do not need full formatting support. Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
1 parent abb3a28 commit 3310382

File tree

9 files changed

+2584
-0
lines changed

9 files changed

+2584
-0
lines changed

doc/reference/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ API Reference
1717
drivers/index.rst
1818
display/index.rst
1919
file_system/index.rst
20+
misc/formatted_output.rst
2021
kernel/index.rst
2122
logging/index.rst
2223
misc/index
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
.. _formatted_output:
2+
3+
Formatted Output
4+
################
5+
6+
Applications as well as Zephyr itself requires infrastructure to format
7+
values for user consumption. The standard C99 library ``*printf()``
8+
functionality fulfills this need for streaming output devices or memory
9+
buffers, but in an embedded system devices may not accept streamed data
10+
and memory may not be available to store the formatted output.
11+
12+
Internal Zephyr API traditionally provided this both for
13+
:c:func:`printk` and for Zephyr's internal minimal libc, but with
14+
separate internal interfaces. Logging, tracing, shell, and other
15+
applications made use of either these APIs or standard libc routines
16+
based on build options.
17+
18+
The :c:func:`cbprintf` public APIs convert C99 format strings and
19+
arguments, providing output produced one character at a time through a
20+
callback mechanism, replacing the original internal functions and
21+
providing support for almost all C99 format specifications. Existing
22+
use of ``s*printf()`` C libraries in Zephyr can be converted to
23+
:c:func:`snprintfcb()` to avoid pulling in libc implementations.
24+
25+
Several Kconfig options control the set of features that are enabled,
26+
allowing some control over features and memory usage:
27+
28+
* :option:`CONFIG_CBPRINTF_FULL_INTEGRAL`
29+
or :option:`CONFIG_CBPRINTF_REDUCED_INTEGRAL`
30+
* :option:`CONFIG_CBPRINTF_FP_SUPPORT`
31+
* :option:`CONFIG_CBPRINTF_FP_A_SUPPORT`
32+
* :option:`CONFIG_CBPRINTF_FP_ALWAYS_A`
33+
* :option:`CONFIG_CBPRINTF_N_SPECIFIER`
34+
35+
:option:`CONFIG_CBPRINTF_LIBC_SUBSTS` can be used to provide functions
36+
that behave like standard libc functions but use the selected cbprintf
37+
formatter rather than pulling in another formatter from libc.
38+
39+
In addition :option:`CONFIG_CBPRINTF_NANO` can be used to revert back to
40+
the very space-optimized but limited formatter used for :c:func:`printk`
41+
before this capability was added.
42+
43+
.. warning::
44+
45+
If :option:`CONFIG_MINIMAL_LIBC` is selected in combination with
46+
:option:`CONFIG_CBPRINTF_NANO` formatting with C standard library
47+
functions like ``printf`` or ``snprintf`` is limited. Among other
48+
things the ``%n`` specifier, most format flags, precision control, and
49+
floating point are not supported.
50+
51+
API Reference
52+
*************
53+
54+
.. doxygengroup:: cbprintf_apis
55+
:project: Zephyr

include/sys/cbprintf.h

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright (c) 2020 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_SYS_CBPRINTF_H_
8+
#define ZEPHYR_INCLUDE_SYS_CBPRINTF_H_
9+
10+
#include <stdarg.h>
11+
#include <stddef.h>
12+
#include <toolchain.h>
13+
14+
#ifdef __cplusplus
15+
extern "C" {
16+
#endif
17+
18+
/**
19+
* @defgroup cbprintf_apis Formatted Output APIs
20+
* @ingroup support_apis
21+
* @{
22+
*/
23+
24+
/** @brief Signature for a cbprintf callback function.
25+
*
26+
* This function expects two parameters:
27+
*
28+
* * @p c a character to output. The output behavior should be as if
29+
* this was cast to an unsigned char.
30+
* * @p ctx a pointer to an object that provides context for the
31+
* output operation.
32+
*
33+
* The declaration does not specify the parameter types. This allows a
34+
* function like @c fputc to be used without requiring all context pointers to
35+
* be to a @c FILE object.
36+
*
37+
* @return the value of @p c cast to an unsigned char then back to
38+
* int, or a negative error code that will be returned from
39+
* cbprintf().
40+
*/
41+
typedef int (*cbprintf_cb)(/* int c, void *ctx */);
42+
43+
/** @brief *printf-like output through a callback.
44+
*
45+
* This is essentially printf() except the output is generated
46+
* character-by-character using the provided @p out function. This allows
47+
* formatting text of unbounded length without incurring the cost of a
48+
* temporary buffer.
49+
*
50+
* All formatting specifiers of C99 are recognized, and most are supported if
51+
* the functionality is enabled.
52+
*
53+
* @note The functionality of this function is significantly reduced
54+
* when `CONFIG_CBPRINTF_NANO` is selected.
55+
*
56+
* @param out the function used to emit each generated character.
57+
*
58+
* @param ctx context provided when invoking out
59+
*
60+
* @param format a standard ISO C format string with characters and conversion
61+
* specifications.
62+
*
63+
* @param ... arguments corresponding to the conversion specifications found
64+
* within @p format.
65+
*
66+
* @return the number of characters printed, or a negative error value
67+
* returned from invoking @p out.
68+
*/
69+
__printf_like(3, 4)
70+
int cbprintf(cbprintf_cb out, void *ctx, const char *format, ...);
71+
72+
/** @brief Calculate the number of words required for arguments to a cbprintf
73+
* format specification.
74+
*
75+
* This can be used in cases where the arguments must be copied off the stack
76+
* into separate storage for processing the conversion in another context.
77+
*
78+
* @note The length returned does not count bytes. It counts native words
79+
* defined as the size of an `int`.
80+
*
81+
* @note If `CONFIG_CBPRINTF_NANO` is selected the count will be incorrect if
82+
* any passed arguments require more than one `int`.
83+
*
84+
* @param format a standard ISO C format string with characters and conversion
85+
* specifications.
86+
*
87+
* @return the number of `int` elements required to provide all arguments
88+
* required for the conversion.
89+
*/
90+
size_t cbprintf_arglen(const char *format);
91+
92+
/** @brief varargs-aware *printf-like output through a callback.
93+
*
94+
* This is essentially vsprintf() except the output is generated
95+
* character-by-character using the provided @p out function. This allows
96+
* formatting text of unbounded length without incurring the cost of a
97+
* temporary buffer.
98+
*
99+
* @note This function is available only when `CONFIG_CBPRINTF_LIBC_SUBSTS` is
100+
* selected.
101+
*
102+
* @note The functionality of this function is significantly reduced when
103+
* `CONFIG_CBPRINTF_NANO` is selected.
104+
*
105+
* @param out the function used to emit each generated character.
106+
*
107+
* @param ctx context provided when invoking out
108+
*
109+
* @param format a standard ISO C format string with characters and conversion
110+
* specifications.
111+
*
112+
* @param ap a reference to the values to be converted.
113+
*
114+
* @return the number of characters generated, or a negative error value
115+
* returned from invoking @p out.
116+
*/
117+
int cbvprintf(cbprintf_cb out, void *ctx, const char *format, va_list ap);
118+
119+
/** @brief snprintf using Zephyrs cbprintf infrastructure.
120+
*
121+
* @note The functionality of this function is significantly reduced
122+
* when `CONFIG_CBPRINTF_NANO` is selected.
123+
*
124+
* @param str where the formatted content should be written
125+
*
126+
* @param size maximum number of chaacters for the formatted output,
127+
* including the terminating null byte.
128+
*
129+
* @param format a standard ISO C format string with characters and
130+
* conversion specifications.
131+
*
132+
* @param ... arguments corresponding to the conversion specifications found
133+
* within @p format.
134+
*
135+
* return The number of characters that would have been written to @p
136+
* str, excluding the terminating null byte. This is greater than the
137+
* number actually written if @p size is too small.
138+
*/
139+
__printf_like(3, 4)
140+
int snprintfcb(char *str, size_t size, const char *format, ...);
141+
142+
/** @brief vsnprintf using Zephyrs cbprintf infrastructure.
143+
*
144+
* @note This function is available only when `CONFIG_CBPRINTF_LIBC_SUBSTS` is
145+
* selected.
146+
*
147+
* @note The functionality of this function is significantly reduced when
148+
* `CONFIG_CBPRINTF_NANO` is selected.
149+
*
150+
* @param str where the formatted content should be written
151+
*
152+
* @param size maximum number of chaacters for the formatted output, including
153+
* the terminating null byte.
154+
*
155+
* @param format a standard ISO C format string with characters and conversion
156+
* specifications.
157+
*
158+
* @param ... arguments corresponding to the conversion specifications found
159+
* within @p format.
160+
*
161+
* @param ap a reference to the values to be converted.
162+
*
163+
* return The number of characters that would have been written to @p
164+
* str, excluding the terminating null byte. This is greater than the
165+
* number actually written if @p size is too small.
166+
*/
167+
int vsnprintfcb(char *str, size_t size, const char *format, va_list ap);
168+
169+
/**
170+
* @}
171+
*/
172+
173+
#ifdef __cplusplus
174+
}
175+
#endif
176+
177+
#endif /* ZEPHYR_INCLUDE_SYS_CBPRINTF_H_ */

lib/os/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
zephyr_sources_ifdef(CONFIG_BASE64 base64.c)
44

55
zephyr_sources(
6+
cbprintf.c
67
crc32_sw.c
78
crc16_sw.c
89
crc8_sw.c
@@ -23,6 +24,9 @@ zephyr_sources(
2324
heap-validate.c
2425
)
2526

27+
zephyr_sources_ifdef(CONFIG_CBPRINTF_COMPLETE cbprintf_complete.c)
28+
zephyr_sources_ifdef(CONFIG_CBPRINTF_NANO cbprintf_nano.c)
29+
2630
zephyr_sources_ifdef(CONFIG_JSON_LIBRARY json.c)
2731

2832
zephyr_sources_ifdef(CONFIG_RING_BUFFER ring_buffer.c)

lib/os/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,6 @@ config PRINTK_SYNC
7272
interleaving with concurrent usage from another CPU or an
7373
preempting interrupt.
7474

75+
rsource "Kconfig.cbprintf"
76+
7577
endmenu

lib/os/Kconfig.cbprintf

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Copyright (c) 2020 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
choice CBPRINTF_IMPLEMENTATION
5+
prompt "Capabilities of cbprintf implementation"
6+
default CBPRINTF_COMPLETE
7+
8+
config CBPRINTF_COMPLETE
9+
bool "All selected features"
10+
help
11+
Select this for an implementation that supports all potential
12+
conversions, with Kconfig options to control availability at build
13+
time.
14+
15+
# 80: -53% / 982 B (80 / 00)
16+
config CBPRINTF_NANO
17+
bool "Space-optimized but feature-limited"
18+
# nano needs to count characters if it's the formatter for libc
19+
select CBPRINTF_LIBC_SUBSTS if MINIMAL_LIBC
20+
help
21+
If selected a completely different implementation of the core
22+
formatting capability is substituted. This has a much smaller code
23+
footprint, but provides fewer capabilities.
24+
25+
endchoice # CBPRINTF_IMPLEMENTATION
26+
27+
choice CBPRINTF_INTEGRAL_CONV
28+
prompt "Control range of convertible integer values"
29+
default CBPRINTF_FULL_INTEGRAL
30+
31+
# 01: 0% / 0 B (01 / 00)
32+
config CBPRINTF_FULL_INTEGRAL
33+
bool "Convert the full range of integer values"
34+
help
35+
Build cbprintf with buffers sized to support converting the full
36+
range of all integral and pointer values.
37+
38+
Selecting this has no effect on code size, but will increase call
39+
stack size by a few words.
40+
41+
# 00:
42+
config CBPRINTF_REDUCED_INTEGRAL
43+
bool "Convert only integer values that fit in 32 bits"
44+
help
45+
Build cbprintf with buffers sized to support converting integer
46+
values with no more than 32 bits.
47+
48+
This will decrease stack space, but affects conversion of any type
49+
with more than 32 bits. This includes not only intmax_t but any
50+
type that can be converted to an integral represention including
51+
size_t and pointers.
52+
53+
With CBPRINTF_COMPLETE conversions that may result in value-specific
54+
truncation are not supported, and the generated text will be the
55+
specification (e.g. %jd).
56+
57+
With CBPRINTF_NANO all conversions will be attempted but values that
58+
cannot fit will be silently truncated.
59+
60+
endchoice
61+
62+
# 02: 82% / 1530 B (02 / 00)
63+
config CBPRINTF_FP_SUPPORT
64+
bool "Enable floating point formatting in cbprintf"
65+
default y if FPU
66+
depends on CBPRINTF_COMPLETE
67+
help
68+
Build the cbprintf utility function with support for floating
69+
point format specifiers. Selecting this increases stack size
70+
requirements slightly, but increases code size significantly.
71+
72+
# 04: 13% / 456 B (07 / 03)
73+
config CBPRINTF_FP_A_SUPPORT
74+
bool "Enable floating point %a conversions"
75+
depends on CBPRINTF_FULL_INTEGRAL
76+
select CBPRINTF_FP_SUPPORT
77+
help
78+
The %a hexadecimal format for floating point value conversion was
79+
added in C99, but the output is not easily understood so it rarely
80+
appears in application code.
81+
82+
Selecting this adds support for the conversion, but increases the
83+
overall code size related to FP support.
84+
85+
# 40: -15% / -508 B (46 / 06)
86+
config CBPRINTF_FP_ALWAYS_A
87+
bool "Select %a format for all floating point specifications"
88+
select CBPRINTF_FP_A_SUPPORT
89+
help
90+
The %a format for floats requires significantly less code than the
91+
standard decimal representations (%f, %e, %g). Selecting this
92+
option implicitly uses %a (or %A) for all decimal floating
93+
conversions. The precision of the original specification is
94+
ignored.
95+
96+
Selecting this decreases code size when FP_SUPPORT is enabled.
97+
98+
# 08: 3% / 60 B (08 / 00)
99+
config CBPRINTF_N_SPECIFIER
100+
bool "Support %n specifications"
101+
depends on CBPRINTF_COMPLETE
102+
default y
103+
help
104+
If selected %n can be used to determine the number of characters
105+
emitted. If enabled there is a small increase in code size.
106+
107+
# 180: 18% / 138 B (180 / 80) [NANO]
108+
config CBPRINTF_LIBC_SUBSTS
109+
bool "Generate C-library compatible functions using cbprintf"
110+
help
111+
If selected wrappers are generated for various C library functions
112+
using the cbprintf formatter underneath. The wrappers use the C
113+
library function name with a cb suffix; e.g. printfcb() or
114+
vsnprintfcb().
115+
116+
When used with CBPRINTF_NANO this increases the implementation code
117+
size by a small amount.

0 commit comments

Comments
 (0)