Zephyr Project API  3.1.0
A Scalable Open Source RTOS
cbprintf_internal.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#ifndef ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_
8#define ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_
9
10#include <errno.h>
11#include <stdarg.h>
12#include <stddef.h>
13#include <stdint.h>
14#include <zephyr/toolchain.h>
15#include <zephyr/sys/util.h>
16#include <zephyr/sys/__assert.h>
17
18/*
19 * Special alignment cases
20 */
21
22#if defined(__i386__)
23/* there are no gaps on the stack */
24#define VA_STACK_ALIGN(type) 1
25#elif defined(__sparc__)
26/* there are no gaps on the stack */
27#define VA_STACK_ALIGN(type) 1
28#elif defined(__x86_64__)
29#define VA_STACK_MIN_ALIGN 8
30#elif defined(__aarch64__)
31#define VA_STACK_MIN_ALIGN 8
32#elif defined(__riscv)
33#define VA_STACK_MIN_ALIGN (__riscv_xlen / 8)
34#endif
35
36/*
37 * Default alignment values if not specified by architecture config
38 */
39
40#ifndef VA_STACK_MIN_ALIGN
41#define VA_STACK_MIN_ALIGN 1
42#endif
43
44#ifndef VA_STACK_ALIGN
45#define VA_STACK_ALIGN(type) MAX(VA_STACK_MIN_ALIGN, __alignof__(type))
46#endif
47
48static inline void z_cbprintf_wcpy(int *dst, int *src, size_t len)
49{
50 for (size_t i = 0; i < len; i++) {
51 dst[i] = src[i];
52 }
53}
54
56
57#ifdef __cplusplus
58extern "C" {
59#endif
60
61
62#if defined(__sparc__)
63/* The SPARC V8 ABI guarantees that the arguments of a variable argument
64 * list function are stored on the stack at addresses which are 32-bit
65 * aligned. It means that variables of type unit64_t and double may not
66 * be properly aligned on the stack.
67 *
68 * The compiler is aware of the ABI and takes care of this. However,
69 * as we are directly accessing the variable argument list here, we need
70 * to take the alignment into consideration and copy 64-bit arguments
71 * as 32-bit words.
72 */
73#define Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY 1
74#else
75#define Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY 0
76#endif
77
84#ifdef __cplusplus
85#define Z_CBPRINTF_IS_PCHAR(x, flags) \
86 z_cbprintf_cxx_is_pchar(x, (flags) & CBPRINTF_PACKAGE_CONST_CHAR_RO)
87#else
88#define Z_CBPRINTF_IS_PCHAR(x, flags) \
89 _Generic((x) + 0, \
90 char * : 1, \
91 const char * : ((flags) & CBPRINTF_PACKAGE_CONST_CHAR_RO) ? 0 : 1, \
92 volatile char * : 1, \
93 const volatile char * : 1, \
94 wchar_t * : 1, \
95 const wchar_t * : ((flags) & CBPRINTF_PACKAGE_CONST_CHAR_RO) ? 0 : 1, \
96 volatile wchar_t * : 1, \
97 const volatile wchar_t * : 1, \
98 default : \
99 0)
100#endif
101
102/* @brief Check if argument is a certain type of char pointer. What exectly is checked
103 * depends on @p flags. If flags is 0 then 1 is returned if @p x is a char pointer.
104 *
105 * @param idx Argument index.
106 * @param x Argument.
107 * @param flags Flags. See @p CBPRINTF_PACKAGE_FLAGS.
108 *
109 * @retval 1 if @p x is char pointer meeting criteria identified by @p flags.
110 * @retval 0 otherwise.
111 */
112#define Z_CBPRINTF_IS_X_PCHAR(idx, x, flags) \
113 (idx < Z_CBPRINTF_PACKAGE_FIRST_RO_STR_CNT_GET(flags) ? \
114 0 : Z_CBPRINTF_IS_PCHAR(x, flags))
115
124#define Z_CBPRINTF_HAS_PCHAR_ARGS(flags, fmt, ...) \
125 (FOR_EACH_IDX_FIXED_ARG(Z_CBPRINTF_IS_X_PCHAR, (+), flags, __VA_ARGS__))
126
127#define Z_CBPRINTF_PCHAR_COUNT(flags, ...) \
128 COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), \
129 (0), \
130 (Z_CBPRINTF_HAS_PCHAR_ARGS(flags, __VA_ARGS__)))
131
140#if Z_C_GENERIC
141#define Z_CBPRINTF_MUST_RUNTIME_PACKAGE(flags, ...) ({\
142 _Pragma("GCC diagnostic push") \
143 _Pragma("GCC diagnostic ignored \"-Wpointer-arith\"") \
144 int _rv; \
145 if ((flags) & CBPRINTF_PACKAGE_ADD_RW_STR_POS) { \
146 _rv = 0; \
147 } else { \
148 _rv = Z_CBPRINTF_PCHAR_COUNT(flags, __VA_ARGS__) > 0 ? 1 : 0; \
149 } \
150 _Pragma("GCC diagnostic pop")\
151 _rv; \
152})
153#else
154#define Z_CBPRINTF_MUST_RUNTIME_PACKAGE(flags, ...) 1
155#endif
156
166#ifdef __cplusplus
167#define Z_CBPRINTF_ARG_SIZE(v) z_cbprintf_cxx_arg_size(v)
168#else
169#define Z_CBPRINTF_ARG_SIZE(v) ({\
170 __auto_type _v = (v) + 0; \
171 /* Static code analysis may complain about unused variable. */ \
172 (void)_v; \
173 size_t _arg_size = _Generic((v), \
174 float : sizeof(double), \
175 default : \
176 sizeof((_v)) \
177 ); \
178 _arg_size; \
179})
180#endif
181
188#ifdef __cplusplus
189#define Z_CBPRINTF_STORE_ARG(buf, arg) z_cbprintf_cxx_store_arg(buf, arg)
190#else
191#define Z_CBPRINTF_STORE_ARG(buf, arg) do { \
192 if (Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY) { \
193 /* If required, copy arguments by word to avoid unaligned access.*/ \
194 __auto_type _v = (arg) + 0; \
195 double _d = _Generic((arg) + 0, \
196 float : (arg) + 0, \
197 default : \
198 0.0); \
199 /* Static code analysis may complain about unused variable. */ \
200 (void)_v; \
201 (void)_d; \
202 size_t arg_size = Z_CBPRINTF_ARG_SIZE(arg); \
203 size_t _wsize = arg_size / sizeof(int); \
204 z_cbprintf_wcpy((int *)buf, \
205 (int *) _Generic((arg) + 0, float : &_d, default : &_v), \
206 _wsize); \
207 } else { \
208 *_Generic((arg) + 0, \
209 char : (int *)buf, \
210 unsigned char: (int *)buf, \
211 short : (int *)buf, \
212 unsigned short : (int *)buf, \
213 int : (int *)buf, \
214 unsigned int : (unsigned int *)buf, \
215 long : (long *)buf, \
216 unsigned long : (unsigned long *)buf, \
217 long long : (long long *)buf, \
218 unsigned long long : (unsigned long long *)buf, \
219 float : (double *)buf, \
220 double : (double *)buf, \
221 long double : (long double *)buf, \
222 default : \
223 (const void **)buf) = arg; \
224 } \
225} while (0)
226#endif
227
234#ifdef __cplusplus
235#define Z_CBPRINTF_ALIGNMENT(_arg) z_cbprintf_cxx_alignment(_arg)
236#else
237#define Z_CBPRINTF_ALIGNMENT(_arg) \
238 MAX(_Generic((_arg) + 0, \
239 float : VA_STACK_ALIGN(double), \
240 double : VA_STACK_ALIGN(double), \
241 long double : VA_STACK_ALIGN(long double), \
242 long long : VA_STACK_ALIGN(long long), \
243 unsigned long long : VA_STACK_ALIGN(long long), \
244 default : \
245 __alignof__((_arg) + 0)), VA_STACK_MIN_ALIGN)
246#endif
247
258#ifdef __cplusplus
259#if defined(__x86_64__) || defined(__riscv) || defined(__aarch64__)
260#define Z_CBPRINTF_IS_LONGDOUBLE(x) 0
261#else
262#define Z_CBPRINTF_IS_LONGDOUBLE(x) z_cbprintf_cxx_is_longdouble(x)
263#endif
264#else
265#define Z_CBPRINTF_IS_LONGDOUBLE(x) \
266 _Generic((x) + 0, long double : 1, default : 0)
267#endif
268
284#define Z_CBPRINTF_PACK_ARG2(arg_idx, _buf, _idx, _align_offset, _max, _arg) \
285do { \
286 BUILD_ASSERT(!((sizeof(double) < VA_STACK_ALIGN(long double)) && \
287 Z_CBPRINTF_IS_LONGDOUBLE(_arg) && \
288 !IS_ENABLED(CONFIG_CBPRINTF_PACKAGE_LONGDOUBLE)),\
289 "Packaging of long double not enabled in Kconfig."); \
290 while (_align_offset % Z_CBPRINTF_ALIGNMENT(_arg)) { \
291 _idx += sizeof(int); \
292 _align_offset += sizeof(int); \
293 } \
294 uint32_t _arg_size = Z_CBPRINTF_ARG_SIZE(_arg); \
295 uint32_t _loc = _idx / sizeof(int); \
296 if (arg_idx < 1 + _fros_cnt) { \
297 if (_ros_pos_en) { \
298 _ros_pos_buf[_ros_pos_idx++] = _loc; \
299 } \
300 } else if (Z_CBPRINTF_IS_PCHAR(_arg, 0)) { \
301 if (_cros_en) { \
302 if (Z_CBPRINTF_IS_X_PCHAR(arg_idx, _arg, _flags)) { \
303 if (_rws_pos_en) { \
304 _rws_buffer[_rws_pos_idx++] = _loc; \
305 } \
306 } else { \
307 if (_ros_pos_en) { \
308 _ros_pos_buf[_ros_pos_idx++] = _loc; \
309 } \
310 } \
311 } else if (_rws_pos_en) { \
312 _rws_buffer[_rws_pos_idx++] = _idx / sizeof(int); \
313 } \
314 } \
315 if (_buf && _idx < (int)_max) { \
316 Z_CBPRINTF_STORE_ARG(&_buf[_idx], _arg); \
317 } \
318 _idx += _arg_size; \
319 _align_offset += _arg_size; \
320} while (0)
321
328#define Z_CBPRINTF_PACK_ARG(arg_idx, arg) \
329 Z_CBPRINTF_PACK_ARG2(arg_idx, _pbuf, _pkg_len, _pkg_offset, _pmax, arg)
330
337struct z_cbprintf_desc {
338 uint8_t len;
339 uint8_t str_cnt; /* Number of appended strings in the package. */
340 uint8_t ro_str_cnt; /* Number of read-only strings, indexes appended to the package.*/
341 uint8_t rw_str_cnt; /* Number of read-write strings, indexes appended to the package.*/
342};
343
345union z_cbprintf_hdr {
346 struct z_cbprintf_desc desc;
347 void *raw;
348};
349
350/* When using clang additional warning needs to be suppressed since each
351 * argument of fmt string is used for sizeof() which results in the warning
352 * if argument is a string literal. Suppression is added here instead of
353 * the macro which generates the warning to not slow down the compiler.
354 */
355#ifdef __clang__
356#define Z_CBPRINTF_SUPPRESS_SIZEOF_ARRAY_DECAY \
357 _Pragma("GCC diagnostic ignored \"-Wsizeof-array-decay\"")
358#else
359#define Z_CBPRINTF_SUPPRESS_SIZEOF_ARRAY_DECAY
360#endif
361
362/* Allocation to avoid using VLA and alloca. Alloc frees space when leaving
363 * a function which can lead to increased stack usage if logging is used
364 * multiple times. VLA is not always available.
365 *
366 * Use large array when optimization is off to avoid increased stack usage.
367 */
368#ifdef CONFIG_NO_OPTIMIZATIONS
369#define Z_CBPRINTF_ON_STACK_ALLOC(_name, _len) \
370 __ASSERT(_len <= 32, "Too many string arguments."); \
371 uint8_t _name##_buf32[32]; \
372 _name = _name##_buf32
373#else
374#define Z_CBPRINTF_ON_STACK_ALLOC(_name, _len) \
375 __ASSERT(_len <= 32, "Too many string arguments."); \
376 uint8_t _name##_buf4[4]; \
377 uint8_t _name##_buf8[8]; \
378 uint8_t _name##_buf12[12]; \
379 uint8_t _name##_buf16[16]; \
380 uint8_t _name##_buf32[32]; \
381 _name = (_len) <= 4 ? _name##_buf4 : \
382 ((_len) <= 8 ? _name##_buf8 : \
383 ((_len) <= 12 ? _name##_buf12 : \
384 ((_len) <= 16 ? _name##_buf16 : \
385 _name##_buf32)))
386#endif
387
403#define Z_CBPRINTF_STATIC_PACKAGE_GENERIC(buf, _inlen, _outlen, _align_offset, \
404 flags, ... /* fmt, ... */) \
405do { \
406 _Pragma("GCC diagnostic push") \
407 _Pragma("GCC diagnostic ignored \"-Wpointer-arith\"") \
408 Z_CBPRINTF_SUPPRESS_SIZEOF_ARRAY_DECAY \
409 BUILD_ASSERT(!IS_ENABLED(CONFIG_XTENSA) || \
410 (IS_ENABLED(CONFIG_XTENSA) && \
411 !(_align_offset % CBPRINTF_PACKAGE_ALIGNMENT)), \
412 "Xtensa requires aligned package."); \
413 BUILD_ASSERT((_align_offset % sizeof(int)) == 0, \
414 "Alignment offset must be multiply of a word."); \
415 IF_ENABLED(CONFIG_CBPRINTF_STATIC_PACKAGE_CHECK_ALIGNMENT, \
416 (__ASSERT(!((uintptr_t)buf & (CBPRINTF_PACKAGE_ALIGNMENT - 1)), \
417 "Buffer must be aligned.");)) \
418 uint32_t _flags = flags; \
419 bool _ros_pos_en = (_flags) & CBPRINTF_PACKAGE_ADD_RO_STR_POS; \
420 bool _rws_pos_en = (_flags) & CBPRINTF_PACKAGE_ADD_RW_STR_POS; \
421 bool _cros_en = (_flags) & CBPRINTF_PACKAGE_CONST_CHAR_RO; \
422 uint8_t *_pbuf = buf; \
423 uint8_t _rws_pos_idx = 0; \
424 uint8_t _ros_pos_idx = 0; \
425 /* Variable holds count of all string pointer arguments. */ \
426 uint8_t _alls_cnt = Z_CBPRINTF_PCHAR_COUNT(0, __VA_ARGS__); \
427 uint8_t _fros_cnt = Z_CBPRINTF_PACKAGE_FIRST_RO_STR_CNT_GET(_flags); \
428 /* Variable holds count of non const string pointers. */ \
429 uint8_t _rws_cnt = _cros_en ? \
430 Z_CBPRINTF_PCHAR_COUNT(_flags, __VA_ARGS__) : _alls_cnt - _fros_cnt; \
431 uint8_t _ros_cnt = _ros_pos_en ? (1 + _alls_cnt - _rws_cnt) : 0; \
432 uint8_t *_ros_pos_buf; \
433 Z_CBPRINTF_ON_STACK_ALLOC(_ros_pos_buf, _ros_cnt); \
434 uint8_t *_rws_buffer; \
435 Z_CBPRINTF_ON_STACK_ALLOC(_rws_buffer, _rws_cnt); \
436 size_t _pmax = (buf != NULL) ? _inlen : INT32_MAX; \
437 int _pkg_len = 0; \
438 int _total_len = 0; \
439 int _pkg_offset = _align_offset; \
440 union z_cbprintf_hdr *_len_loc; \
441 /* If string has rw string arguments CBPRINTF_PACKAGE_ADD_RW_STR_POS is a must. */ \
442 if (_rws_cnt && !((_flags) & CBPRINTF_PACKAGE_ADD_RW_STR_POS)) { \
443 _outlen = -EINVAL; \
444 break; \
445 } \
446 /* package starts with string address and field with length */ \
447 if (_pmax < sizeof(union z_cbprintf_hdr)) { \
448 _outlen = -ENOSPC; \
449 break; \
450 } \
451 _len_loc = (union z_cbprintf_hdr *)_pbuf; \
452 _pkg_len += sizeof(union z_cbprintf_hdr); \
453 _pkg_offset += sizeof(union z_cbprintf_hdr); \
454 /* Pack remaining arguments */\
455 FOR_EACH_IDX(Z_CBPRINTF_PACK_ARG, (;), __VA_ARGS__);\
456 _total_len = _pkg_len; \
457 /* Append string indexes to the package. */ \
458 _total_len += _ros_cnt; \
459 _total_len += _rws_cnt; \
460 if (_pbuf) { \
461 /* Append string locations. */ \
462 uint8_t *_pbuf_loc = &_pbuf[_pkg_len]; \
463 for (size_t i = 0; i < _ros_cnt; i++) { \
464 *_pbuf_loc++ = _ros_pos_buf[i]; \
465 } \
466 for (size_t i = 0; i < _rws_cnt; i++) { \
467 *_pbuf_loc++ = _rws_buffer[i]; \
468 } \
469 } \
470 /* Store length */ \
471 _outlen = (_total_len > (int)_pmax) ? -ENOSPC : _total_len; \
472 /* Store length in the header, set number of dumped strings to 0 */ \
473 if (_pbuf) { \
474 union z_cbprintf_hdr hdr = { \
475 .desc = { \
476 .len = (uint8_t)(_pkg_len / sizeof(int)), \
477 .str_cnt = 0, \
478 .ro_str_cnt = _ros_cnt, \
479 .rw_str_cnt = _rws_cnt, \
480 } \
481 }; \
482 *_len_loc = hdr; \
483 } \
484 _Pragma("GCC diagnostic pop") \
485} while (0)
486
487#if Z_C_GENERIC
488#define Z_CBPRINTF_STATIC_PACKAGE(packaged, inlen, outlen, align_offset, flags, \
489 ... /* fmt, ... */) \
490 Z_CBPRINTF_STATIC_PACKAGE_GENERIC(packaged, inlen, outlen, \
491 align_offset, flags, __VA_ARGS__)
492#else
493#define Z_CBPRINTF_STATIC_PACKAGE(packaged, inlen, outlen, align_offset, flags, \
494 ... /* fmt, ... */) \
495do { \
496 /* Small trick needed to avoid warning on always true */ \
497 if (((uintptr_t)packaged + 1) != 1) { \
498 outlen = cbprintf_package(packaged, inlen, flags, __VA_ARGS__); \
499 } else { \
500 outlen = cbprintf_package(NULL, align_offset, flags, __VA_ARGS__); \
501 } \
502} while (0)
503#endif /* Z_C_GENERIC */
504
505#ifdef __cplusplus
506}
507#endif
508
509
510#endif /* ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_ */
System error numbers.
__UINT8_TYPE__ uint8_t
Definition: stdint.h:88
Macros to abstract toolchain specific capabilities.
Misc utilities.