mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'fix/log-dram-early-direct-rom-printf' into 'master'
fix(esp_log): Add Kconfig option to disable usage of esp_rom_vprintf when this function is in IRAM and not ROM Closes IDFGH-17376 See merge request espressif/esp-idf!46640
This commit is contained in:
@@ -52,6 +52,30 @@ menu "Log"
|
||||
|
||||
rsource "./Kconfig.settings"
|
||||
|
||||
config LOG_API_CONSTRAINED_ENV_SAFE
|
||||
bool "Enable safe ESP_LOGx fallback in constrained environments (ISR, cache disabled)"
|
||||
depends on LOG_VERSION_2 && !ESP_ROM_HAS_VPRINTF_FUNC && !LOG_MODE_BINARY_EN
|
||||
default y
|
||||
help
|
||||
This option is only relevant on chips where ets_vprintf is NOT in ROM
|
||||
|
||||
When enabled, Log V2 routes ESP_DRAM_LOGx, ESP_EARLY_LOGx, and runtime
|
||||
constrained-environment log calls (ISR, cache disabled) through esp_log(),
|
||||
which uses esp_rom_vprintf for formatting.
|
||||
|
||||
When disabled, ESP_DRAM_LOGx and ESP_EARLY_LOGx expand directly
|
||||
to esp_rom_printf (a true ROM function with zero IRAM cost), bypassing
|
||||
esp_log(). Normal ESP_LOGx calls that happen to run in a constrained
|
||||
environment (ISR, cache disabled) will use the standard vprintf function
|
||||
instead of esp_rom_vprintf; if the standard vprintf resides in flash,
|
||||
such calls may crash. Use ESP_DRAM_LOGx for any logging that must work
|
||||
with cache disabled or from an ISR.
|
||||
|
||||
Disable this option to save ~1.2 KB of IRAM on memory-constrained devices.
|
||||
Enable it if you need ESP_LOGx to safely fall back to ROM-based printing
|
||||
in ISRs or with cache disabled, or if you need constrained-environment
|
||||
logs in binary log format.
|
||||
|
||||
config LOG_IN_IRAM
|
||||
bool "Place logging functions in IRAM" if SPI_FLASH_AUTO_SUSPEND
|
||||
default y
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -247,14 +247,14 @@ void esp_log_va(esp_log_config_t config, const char *tag, const char *format, va
|
||||
|
||||
#if defined(__cplusplus) && (__cplusplus > 201703L)
|
||||
#define ESP_LOG_EARLY_IMPL(tag, format, configs, log_tag_letter, ...) do { \
|
||||
if (ESP_LOG_VERSION == 1) { \
|
||||
if (ESP_LOG_VERSION == 1 || (ESP_LOG_VERSION == 2 && !ESP_LOG_API_CONSTRAINED_ENV_SAFE)) { \
|
||||
if (_ESP_LOG_EARLY_ENABLED(configs)) { esp_rom_printf(LOG_FORMAT(log_tag_letter, format), esp_log_timestamp(), tag __VA_OPT__(,) __VA_ARGS__); } \
|
||||
} else { \
|
||||
if (ESP_LOG_ENABLED(configs)) { esp_log(ESP_LOG_CONFIG_INIT((configs) | ESP_LOG_CONFIGS_DEFAULT | ESP_LOG_CONFIG_CONSTRAINED_ENV), tag, ESP_LOG_ATTR_STR(format) ESP_LOG_ARGS(__VA_ARGS__)); } \
|
||||
} } while(0)
|
||||
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
|
||||
#define ESP_LOG_EARLY_IMPL(tag, format, configs, log_tag_letter, ...) do { \
|
||||
if (ESP_LOG_VERSION == 1) { \
|
||||
if (ESP_LOG_VERSION == 1 || (ESP_LOG_VERSION == 2 && !ESP_LOG_API_CONSTRAINED_ENV_SAFE)) { \
|
||||
if (_ESP_LOG_EARLY_ENABLED(configs)) { esp_rom_printf(LOG_FORMAT(log_tag_letter, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } \
|
||||
} else { \
|
||||
if (ESP_LOG_ENABLED(configs)) { esp_log(ESP_LOG_CONFIG_INIT((configs) | ESP_LOG_CONFIGS_DEFAULT | ESP_LOG_CONFIG_CONSTRAINED_ENV), tag, ESP_LOG_ATTR_STR(format) ESP_LOG_ARGS(__VA_ARGS__)); } \
|
||||
@@ -263,14 +263,14 @@ void esp_log_va(esp_log_config_t config, const char *tag, const char *format, va
|
||||
|
||||
#if defined(__cplusplus) && (__cplusplus > 201703L)
|
||||
#define ESP_DRAM_LOG_IMPL(tag, format, configs, log_tag_letter, ...) do { \
|
||||
if (ESP_LOG_VERSION == 1) { \
|
||||
if (ESP_LOG_VERSION == 1 || (ESP_LOG_VERSION == 2 && !ESP_LOG_API_CONSTRAINED_ENV_SAFE)) { \
|
||||
if (_ESP_LOG_EARLY_ENABLED(configs)) { esp_rom_printf(_ESP_LOG_DRAM_LOG_FORMAT(log_tag_letter, format), tag __VA_OPT__(,) __VA_ARGS__); } \
|
||||
} else { \
|
||||
if (ESP_LOG_ENABLED(configs)) { esp_log(ESP_LOG_CONFIG_INIT((configs) | ESP_LOG_CONFIGS_DEFAULT | ESP_LOG_CONFIG_CONSTRAINED_ENV | ESP_LOG_CONFIG_DIS_COLOR | ESP_LOG_CONFIG_DIS_TIMESTAMP), tag, ESP_LOG_ATTR_DRAM_STR(format) ESP_LOG_ARGS(__VA_ARGS__)); } \
|
||||
} } while(0)
|
||||
#else // !(defined(__cplusplus) && (__cplusplus > 201703L))
|
||||
#define ESP_DRAM_LOG_IMPL(tag, format, configs, log_tag_letter, ...) do { \
|
||||
if (ESP_LOG_VERSION == 1) { \
|
||||
if (ESP_LOG_VERSION == 1 || (ESP_LOG_VERSION == 2 && !ESP_LOG_API_CONSTRAINED_ENV_SAFE)) { \
|
||||
if (_ESP_LOG_EARLY_ENABLED(configs)) { esp_rom_printf(_ESP_LOG_DRAM_LOG_FORMAT(log_tag_letter, format), tag, ##__VA_ARGS__); } \
|
||||
} else { \
|
||||
if (ESP_LOG_ENABLED(configs)) { esp_log(ESP_LOG_CONFIG_INIT((configs) | ESP_LOG_CONFIGS_DEFAULT | ESP_LOG_CONFIG_CONSTRAINED_ENV | ESP_LOG_CONFIG_DIS_COLOR | ESP_LOG_CONFIG_DIS_TIMESTAMP), tag, ESP_LOG_ATTR_DRAM_STR(format) ESP_LOG_ARGS(__VA_ARGS__)); } \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -111,6 +111,25 @@ extern "C" {
|
||||
#ifndef ESP_LOG_FORMATTING_DISABLED
|
||||
#define ESP_LOG_FORMATTING_DISABLED (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This define controls whether esp_rom_vprintf is used as the fallback print function in constrained
|
||||
* environments (ISR, cache disabled, early startup). When set to 0, esp_rom_vprintf is never referenced,
|
||||
* saving ~1.2 KB of IRAM on chips where it is not a true ROM function.
|
||||
* ESP_DRAM_LOGx and ESP_EARLY_LOGx will use esp_rom_printf directly instead.
|
||||
*
|
||||
* On chips where ets_vprintf is in ROM (ESP32-S2, ESP32-C2), this is always enabled
|
||||
* since there is no IRAM cost.
|
||||
*/
|
||||
#if BOOTLOADER_BUILD || CONFIG_ESP_ROM_HAS_VPRINTF_FUNC
|
||||
#define ESP_LOG_API_CONSTRAINED_ENV_SAFE (1)
|
||||
#else
|
||||
#if defined(CONFIG_LOG_API_CONSTRAINED_ENV_SAFE) || defined(CONFIG_LOG_MODE_BINARY_EN)
|
||||
#define ESP_LOG_API_CONSTRAINED_ENV_SAFE (1)
|
||||
#else
|
||||
#define ESP_LOG_API_CONSTRAINED_ENV_SAFE (0)
|
||||
#endif
|
||||
#endif
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,11 +32,15 @@ __attribute__((always_inline)) static inline void esp_log_vprintf(esp_log_config
|
||||
#else // APP
|
||||
extern vprintf_like_t esp_log_vprint_func;
|
||||
#if ESP_LOG_VERSION == 2
|
||||
#if CONFIG_LOG_API_CONSTRAINED_ENV_SAFE
|
||||
vprintf_like_t vprint_func[2] = {
|
||||
esp_log_vprint_func,
|
||||
esp_rom_vprintf,
|
||||
};
|
||||
vprint_func[config.opts.constrained_env](format, args);
|
||||
#else
|
||||
esp_log_vprint_func(format, args);
|
||||
#endif // CONFIG_LOG_API_CONSTRAINED_ENV_SAFE
|
||||
#else // ESP_LOG_VERSION == 1
|
||||
esp_log_vprint_func(format, args);
|
||||
#endif // ESP_LOG_VERSION == 1
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "esp_timer.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_LOG_API_CONSTRAINED_ENV_SAFE
|
||||
|
||||
#if CONFIG_LOG_TAG_LEVEL_IMPL_NONE
|
||||
#define EXPECTED_US 3
|
||||
#define DELTA_US 3
|
||||
@@ -51,7 +53,11 @@ TEST_CASE("test master logging level performance", "[log]")
|
||||
#if ESP_LOG_VERSION == 1
|
||||
const int typical_value = 150;
|
||||
#else // ESP_LOG_VERSION == 2
|
||||
#if CONFIG_LOG_API_CONSTRAINED_ENV_SAFE
|
||||
const int typical_value = 400;
|
||||
#else
|
||||
const int typical_value = 250;
|
||||
#endif // CONFIG_LOG_API_CONSTRAINED_ENV_SAFE
|
||||
#endif // ESP_LOG_VERSION == 2
|
||||
TEST_ASSERT_INT_WITHIN(100, typical_value, calc_time_of_logging(ITERATIONS));
|
||||
#else
|
||||
@@ -68,3 +74,4 @@ TEST_CASE("test master logging level performance", "[log]")
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
ESP_LOGI(TAG, "End");
|
||||
}
|
||||
#endif // CONFIG_LOG_API_CONSTRAINED_ENV_SAFE
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "esp_rom_serial_output.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
@@ -152,3 +153,83 @@ TEST_CASE("LOG_LOCAL_LEVEL can be re-defined", "[log]")
|
||||
esp_rom_install_uart_printf();
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
}
|
||||
|
||||
#if CONFIG_LOG_VERSION_2
|
||||
/*
|
||||
* Test ESP_LOG behavior in constrained environments (ISR, cache disabled).
|
||||
*
|
||||
* CONFIG_LOG_API_CONSTRAINED_ENV_SAFE controls how esp_log_vprintf() prints
|
||||
* when the constrained_env flag is set:
|
||||
* - Enabled: esp_log() uses esp_rom_vprintf (in IRAM) for output.
|
||||
* ESP_LOG is safe to call from ISR or with cache disabled.
|
||||
* - Disabled: esp_log() uses the standard vprintf callback (typically in flash).
|
||||
* ESP_LOG from ISR or with cache disabled will crash because the
|
||||
* print function is not in internal memory.
|
||||
*
|
||||
* We install print_to_buffer as the vprintf callback and disconnect the putc
|
||||
* channel (used by esp_rom_vprintf). Then we call esp_log() with
|
||||
* constrained_env=true and check where the output went:
|
||||
* - Enabled: output goes to esp_rom_vprintf -> putc channel (disconnected)
|
||||
* -> buffer stays empty.
|
||||
* - Disabled: output goes to the vprintf callback (print_to_buffer)
|
||||
* -> buffer contains the message.
|
||||
*/
|
||||
TEST_CASE("ESP_LOG constrained_env uses rom vprintf only when constrained_env_safe is enabled", "[log]")
|
||||
{
|
||||
vprintf_like_t old_vprintf = esp_log_set_vprintf(print_to_buffer);
|
||||
|
||||
/* Simulate a log call from a constrained environment (ISR / cache disabled) */
|
||||
esp_log_config_t cfg = ESP_LOG_CONFIG_INIT(ESP_LOG_INFO
|
||||
| ESP_LOG_CONFIG_CONSTRAINED_ENV);
|
||||
|
||||
/* Disconnect the putc channel so esp_rom_vprintf output is not captured.
|
||||
* esp_rom_install_uart_printf() alone does not reset the software _putc1
|
||||
* on chips without ESP_ROM_HAS_ETS_PRINTF_BUG (e.g. ESP32), so explicitly
|
||||
* restore the default putc via esp_rom_install_channel_putc(). */
|
||||
esp_rom_install_channel_putc(1, esp_rom_output_putc);
|
||||
esp_rom_install_uart_printf();
|
||||
reset_buffer();
|
||||
esp_log(cfg, TAG1, "constrained %d", 42);
|
||||
|
||||
#if CONFIG_LOG_API_CONSTRAINED_ENV_SAFE
|
||||
/* esp_rom_vprintf was used (IRAM-safe) -> nothing in our vprintf buffer */
|
||||
TEST_ASSERT_EQUAL(0, get_counter());
|
||||
#else
|
||||
/* Standard vprintf was used (flash-based, would crash in real ISR/cache-off) */
|
||||
TEST_ASSERT_NOT_NULL(strstr(get_buffer(), "constrained 42"));
|
||||
#endif
|
||||
|
||||
/* Restore */
|
||||
esp_log_set_vprintf(old_vprintf);
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
}
|
||||
|
||||
#if CONFIG_LOG_API_CONSTRAINED_ENV_SAFE
|
||||
/*
|
||||
* When constrained_env_safe is enabled and LOG_IN_IRAM=y (default), esp_log()
|
||||
* code is in IRAM and uses esp_rom_vprintf (ROM) for output. This means
|
||||
* esp_log() can safely be called with cache disabled, as long as the format
|
||||
* string and tag are also in DRAM/IRAM.
|
||||
*
|
||||
* Verify this by actually disabling flash cache and calling esp_log() with
|
||||
* constrained_env=true and a DRAM format string. If it doesn't crash, the
|
||||
* IRAM path is correctly used.
|
||||
*/
|
||||
static void IRAM_ATTR test_log_cache_disabled_cb(void *arg)
|
||||
{
|
||||
/* All strings must be in DRAM since flash is inaccessible */
|
||||
static const DRAM_ATTR char tag[] = "test";
|
||||
static const DRAM_ATTR char fmt[] = "cache_off %d\n";
|
||||
esp_log_config_t cfg = ESP_LOG_CONFIG_INIT(ESP_LOG_INFO
|
||||
| ESP_LOG_CONFIG_CONSTRAINED_ENV);
|
||||
esp_log(cfg, tag, fmt, 99);
|
||||
}
|
||||
|
||||
TEST_CASE("ESP_LOG with constrained_env_safe works when cache is disabled", "[log]")
|
||||
{
|
||||
/* If esp_log() tries to use a flash-resident vprintf, this will crash */
|
||||
unity_utils_run_cache_disable_stub(test_log_cache_disabled_cb, NULL);
|
||||
/* Reaching here without a crash proves the IRAM/ROM path was used */
|
||||
}
|
||||
#endif // CONFIG_LOG_API_CONSTRAINED_ENV_SAFE
|
||||
#endif // CONFIG_LOG_VERSION_2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
@@ -6,6 +6,15 @@ from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'default',
|
||||
'v2_constrained_env_safe',
|
||||
'v2_constrained_env_unsafe',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32'], indirect=['target'])
|
||||
def test_esp_log(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# This is left intentionally blank. It inherits all configurations from sdkconfig.defaults
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_LOG_VERSION_2=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_LOG_VERSION_2=y
|
||||
CONFIG_LOG_API_CONSTRAINED_ENV_SAFE=n
|
||||
@@ -194,6 +194,7 @@ The following options will reduce IRAM usage of some ESP-IDF features:
|
||||
- Disable :ref:`CONFIG_LIBC_LOCKS_PLACE_IN_IRAM` if no ISRs that run while cache is disabled (i.e. IRAM ISRs) use libc lock APIs.
|
||||
:CONFIG_ESP_ROM_HAS_SUBOPTIMAL_NEWLIB_ON_MISALIGNED_MEMORY: - Disable :ref:`CONFIG_LIBC_OPTIMIZED_MISALIGNED_ACCESS` to save approximately 1000 bytes of IRAM, at the cost of reduced performance.
|
||||
:SOC_SPIRAM_SUPPORTED: - Enable :ref:`CONFIG_ESP_EVENT_LOOP_IN_EXT_RAM` to force ``esp_event`` to place event loop related allocations in external RAM instead of internal RAM.
|
||||
:not CONFIG_ESP_ROM_HAS_VPRINTF_FUNC: - When using **Log V2**, disable :ref:`CONFIG_LOG_API_CONSTRAINED_ENV_SAFE` to remove ``esp_rom_vprintf`` from IRAM, saving ~1.2 KB. This means ``ESP_LOGx`` will no longer safely fall back to ROM-based printing in ISRs or with cache disabled. Use ``ESP_DRAM_LOGx`` explicitly for constrained-environment logging. See :doc:`/api-reference/system/log` for details.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
|
||||
@@ -656,6 +656,23 @@ The following measurements were performed using the ``esp_timer`` example with d
|
||||
|
||||
Enabling **Log V2** increases IRAM usage while reducing the overall application binary size, Flash code, and data usage.
|
||||
|
||||
.. only:: not CONFIG_ESP_ROM_HAS_VPRINTF_FUNC
|
||||
|
||||
Reducing IRAM Usage in Log V2
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The IRAM increase in **Log V2** is primarily caused by ``esp_rom_vprintf``, which is compiled into IRAM (~1.2 KB) on {IDF_TARGET_NAME}. This function is referenced as the fallback formatter for constrained environments (ISR, cache disabled) in the ``esp_log_vprintf()`` inline function.
|
||||
|
||||
On chips where IRAM and DRAM share the same memory pool, this also reduces available heap by the same amount.
|
||||
|
||||
To eliminate this cost, disable :ref:`CONFIG_LOG_API_CONSTRAINED_ENV_SAFE` (enabled by default). When disabled:
|
||||
|
||||
- ``ESP_DRAM_LOGx`` and ``ESP_EARLY_LOGx`` expand directly to ``esp_rom_printf()`` (a true ROM function, zero IRAM cost), bypassing the ``esp_log()`` pipeline entirely.
|
||||
- Normal ``ESP_LOGx`` calls in constrained environments (ISR, cache disabled) will use the standard ``vprintf`` function. If ``vprintf`` resides in flash, such calls may crash. Use ``ESP_DRAM_LOGx`` for any logging that must work with cache disabled or from an ISR.
|
||||
- ``esp_rom_vprintf`` is never referenced, so the linker excludes it from the binary.
|
||||
|
||||
When enabled, the original **Log V2** behavior is preserved: all constrained-environment logs route through ``esp_log()`` and use ``esp_rom_vprintf`` as the formatter for early/DRAM logs.
|
||||
|
||||
Logging to Host via JTAG
|
||||
------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user