mirror of
https://github.com/esphome/esphome.git
synced 2026-05-28 21:59:59 +08:00
[esp32] Drop printf wrap on IDF 6.0+ (picolibc no longer needs it) (#16189)
This commit is contained in:
committed by
Jesse Hills
parent
d9c22d6b56
commit
0418f2138a
@@ -1740,7 +1740,17 @@ async def to_code(config):
|
|||||||
|
|
||||||
# Wrap FILE*-based printf functions to eliminate newlib's _vfprintf_r
|
# Wrap FILE*-based printf functions to eliminate newlib's _vfprintf_r
|
||||||
# (~11 KB). See printf_stubs.cpp for implementation.
|
# (~11 KB). See printf_stubs.cpp for implementation.
|
||||||
if conf[CONF_ADVANCED][CONF_ENABLE_FULL_PRINTF]:
|
#
|
||||||
|
# The wrap is only beneficial against newlib. Picolibc's tinystdio
|
||||||
|
# implements vsnprintf by building a string-output FILE and calling
|
||||||
|
# vfprintf, so vfprintf is unconditionally linked in by any caller
|
||||||
|
# of snprintf/vsnprintf — effectively every build — and the wrap
|
||||||
|
# saves nothing while costing ~170 B of shim. IDF 5.x defaults to
|
||||||
|
# newlib on every variant; IDF 6.0+ switches to picolibc on every
|
||||||
|
# variant.
|
||||||
|
if conf[CONF_ADVANCED][CONF_ENABLE_FULL_PRINTF] or idf_version() >= cv.Version(
|
||||||
|
6, 0, 0
|
||||||
|
):
|
||||||
cg.add_define("USE_FULL_PRINTF")
|
cg.add_define("USE_FULL_PRINTF")
|
||||||
else:
|
else:
|
||||||
for symbol in ("vprintf", "printf", "fprintf", "vfprintf"):
|
for symbol in ("vprintf", "printf", "fprintf", "vfprintf"):
|
||||||
|
|||||||
@@ -1,91 +1,48 @@
|
|||||||
/*
|
/*
|
||||||
* Linker wrap stubs for FILE*-based printf functions.
|
* Linker wrap stubs for FILE*-based printf functions (newlib only).
|
||||||
*
|
*
|
||||||
* ESP-IDF SDK components (gpio driver, ringbuf, log_write) reference
|
* ESP-IDF SDK components (gpio driver, ringbuf, log_write) reference
|
||||||
* fprintf(), printf(), vprintf(), and vfprintf() which pull in the full
|
* fprintf(), printf(), vprintf(), and vfprintf(), which on newlib pull
|
||||||
* printf implementation (~11 KB on newlib's _vfprintf_r, ~2.8 KB on
|
* in _vfprintf_r (~11 KB) — a separate implementation from the one used
|
||||||
* picolibc's vfprintf). This is a separate implementation from the one
|
* by snprintf/vsnprintf that handles FILE* stream I/O with buffering.
|
||||||
* used by snprintf/vsnprintf that handles FILE* stream I/O with buffering
|
|
||||||
* and locking.
|
|
||||||
*
|
*
|
||||||
* ESPHome replaces the ESP-IDF log handler via esp_log_set_vprintf_(),
|
* ESPHome replaces the ESP-IDF log handler via esp_log_set_vprintf_(),
|
||||||
* so the SDK's vprintf() path is dead code at runtime. The fprintf()
|
* so the SDK's vprintf() path is dead code at runtime. The fprintf()
|
||||||
* and printf() calls in SDK components are only in debug/assert paths
|
* and printf() calls in SDK components are only in debug/assert paths
|
||||||
* (gpio_dump_io_configuration, ringbuf diagnostics) that are either
|
* (gpio_dump_io_configuration, ringbuf diagnostics) that are either
|
||||||
* GC'd or never called. Crash backtraces and panic output are
|
* GC'd or never called. Crash backtraces and panic output are
|
||||||
* unaffected; they use esp_rom_printf() which is a ROM function
|
* unaffected; they use esp_rom_printf() which is a ROM function and
|
||||||
* and does not go through libc.
|
* does not go through libc.
|
||||||
*
|
*
|
||||||
* On picolibc (default for IDF >= 5 on RISC-V, IDF >= 6 everywhere) we
|
* This wrap is newlib-only. On picolibc, vsnprintf is implemented as
|
||||||
* route output through a stack-allocated cookie FILE that forwards each
|
* vfprintf into a string-output FILE, so vfprintf is unconditionally
|
||||||
* byte to the real target stream via fputc(). Picolibc's tinystdio
|
* linked in by any caller of snprintf/vsnprintf and the wrap can never
|
||||||
* vfprintf walks the FILE::put callback one character at a time, so this
|
* elide it — it just adds shim cost. Codegen forces USE_FULL_PRINTF
|
||||||
* costs ~32 bytes of stack for the cookie struct vs. a 512-byte format
|
* on picolibc builds (IDF 6.0+ on all variants) so this file compiles
|
||||||
* buffer. The buffered path overflows the loopTask stack on IDF 6.
|
* to nothing there; the #error below catches a desynchronised gate.
|
||||||
*
|
*
|
||||||
* On newlib (IDF <= 5 on Xtensa) we keep the original snprintf-then-fwrite
|
* Saves ~11 KB of flash on newlib.
|
||||||
* path because that loopTask stack budget has plenty of headroom for the
|
|
||||||
* 512-byte buffer; the picolibc-only crash above does not affect it.
|
|
||||||
*
|
*
|
||||||
* Saves ~11 KB of flash on newlib, ~2.8 KB on picolibc.
|
* To disable this wrap on newlib, set enable_full_printf: true in the
|
||||||
*
|
* esp32 advanced config section.
|
||||||
* To disable these wraps, set enable_full_printf: true in the esp32
|
|
||||||
* advanced config section.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(USE_ESP_IDF) && !defined(USE_FULL_PRINTF)
|
#if defined(USE_ESP_IDF) && !defined(USE_FULL_PRINTF)
|
||||||
|
|
||||||
|
#ifdef __PICOLIBC__
|
||||||
|
#error "printf wrap is net-negative on picolibc; codegen should set USE_FULL_PRINTF"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifndef __PICOLIBC__
|
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome::esp32 {}
|
namespace esphome::esp32 {}
|
||||||
|
|
||||||
// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
#ifdef __PICOLIBC__
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
extern int __real_vfprintf(FILE *stream, const char *fmt, va_list ap);
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct CookieFile {
|
|
||||||
FILE base;
|
|
||||||
FILE *target;
|
|
||||||
};
|
|
||||||
|
|
||||||
// cookie_put() recovers CookieFile* from FILE* via reinterpret_cast, which is
|
|
||||||
// only well-defined when FILE is the first member at offset 0 and CookieFile
|
|
||||||
// is standard-layout.
|
|
||||||
static_assert(offsetof(CookieFile, base) == 0, "FILE must be the first member of CookieFile");
|
|
||||||
static_assert(std::is_standard_layout<CookieFile>::value, "CookieFile must be standard-layout");
|
|
||||||
|
|
||||||
int cookie_put(char c, FILE *stream) {
|
|
||||||
auto *cookie = reinterpret_cast<CookieFile *>(stream);
|
|
||||||
return fputc(static_cast<unsigned char>(c), cookie->target);
|
|
||||||
}
|
|
||||||
|
|
||||||
const FILE COOKIE_FILE_TEMPLATE = FDEV_SETUP_STREAM(cookie_put, nullptr, nullptr, _FDEV_SETUP_WRITE);
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int __wrap_vfprintf(FILE *stream, const char *fmt, va_list ap) {
|
|
||||||
CookieFile cookie;
|
|
||||||
cookie.base = COOKIE_FILE_TEMPLATE;
|
|
||||||
cookie.target = stream;
|
|
||||||
return __real_vfprintf(&cookie.base, fmt, ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
int __wrap_vprintf(const char *fmt, va_list ap) { return __wrap_vfprintf(stdout, fmt, ap); }
|
|
||||||
|
|
||||||
#else // !__PICOLIBC__
|
|
||||||
|
|
||||||
static constexpr size_t PRINTF_BUFFER_SIZE = 512;
|
static constexpr size_t PRINTF_BUFFER_SIZE = 512;
|
||||||
|
|
||||||
// These stubs are essentially dead code at runtime — ESPHome replaces the
|
// These stubs are essentially dead code at runtime — ESPHome replaces the
|
||||||
@@ -117,8 +74,6 @@ int __wrap_vfprintf(FILE *stream, const char *fmt, va_list ap) {
|
|||||||
return write_printf_buffer(stream, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
|
return write_printf_buffer(stream, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __PICOLIBC__
|
|
||||||
|
|
||||||
int __wrap_printf(const char *fmt, ...) {
|
int __wrap_printf(const char *fmt, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
|
|||||||
Reference in New Issue
Block a user