乐鑫技术交流
直播中

飘逸的D

12年用户 800经验值
私信 关注
[问答]

IDF4.2.1的编译器优化bug导致panic怎么处理?

芯片:ESP32-D0WDQ6
IDF版本:v4.2.1
开发环境:tools for windows,cmake。


这几天在写一个项目,同时用到uart和ota功能。当uart发送数据时,同时进行ota升级,就会产生panic。
经过长时间的追踪和分析,已经确认了bug原因,并采取了临时补救措施。
下面是错误分析过程:
espesp-idfcomponentsdriveruart.c
这个文件里有uart_rx_intr_handler_default函数,它是布局在IRAM里的代码,这是为了在cache禁用期间能处理uart中断请求。
它有一个分支,执行了uart_hal_is_tx_idle宏(其实这个宏是uart_ll_is_tx_idle的别名),请看下面:
espesp-idfcomponentssocincludehaluart_hal.h
Code: Select all
#define uart_hal_is_tx_idle(hal)  uart_ll_is_tx_idle((hal)->dev)
而uart_ll_is_tx_idle定义在如下文件:
espesp-idfcomponentssocsrcesp32includehaluart_ll.h
Code: Select all
static inline bool uart_ll_is_tx_idle(uart_dev_t *hw){    typeof(hw->status) status = hw->status;    return ((status.txfifo_cnt == 0) && (status.st_utx_out == 0));}
它是一个inline函数。我们都认为inline关键字,会告诉编译器内联此函数,于是它的代码会被展开到调用方函数体内,
比如uart_rx_intr_handler_default函数在IRAM内,那么uart_ll_is_tx_idle的逻辑也就在IRAM里,事实上这是理想状态而已。
就是这个假设引起了bug!
我观察的现象为:uart.c里有三处代码调用了uart_ll_is_tx_idle内联函数,但是在编译器配置为-Os的情况下,它自作聪明地把uart_ll_is_tx_idle函数编译成非内联函数!估计3处调用,编译为非内联可以节省空间吧?于是uart_ll_is_tx_idle函数实体就被默认安排在flash地址空间了,不再是安全的IRAM里!这就导致uart中断里执行了flash代码,而同时ota禁用cache的话就发生panic了。
bug复现方式就是把sdkconfig文件里优化配置为CONFIG_COMPILER_OPTIMIZATION_SIZE=y,然后观察uart_ll_is_tx_idle是否内联;在readelf -s导出的标号里,也能看到uart_ll_is_tx_idle,并且是在flash地址段。
我的临时解决方法是:在uart.c文件里加一行声明,强制uart_ll_is_tx_idle内联(它比关键字inline靠谱!)Code: Select all
static inline bool uart_ll_is_tx_idle(uart_dev_t *hw) __attribute((always_inline));
或者把CONFIG_COMPILER_OPTIMIZATION_SIZE取消,设置为CONFIG_COMPILER_OPTIMIZATION_PERF=y也行。
但是通过改编译优化等级来解决问题并不科学,这说明代码经不起优化,而且指不定引入其它隐秘的bug呢?

回帖(1)

微风挽雨

2024-6-22 15:23:13
1. **更新ESP-IDF版本**:首先,建议您尝试更新ESP-IDF到最新版本(例如v4.4或更高版本),因为新版本可能已经修复了这个问题。您可以访问ESP-IDF的GitHub仓库(https://github.com/espressif/esp-idf)以获取最新版本。

2. **禁用优化**:如果更新ESP-IDF版本后问题仍然存在,您可以尝试在编译时禁用优化。在CMakeLists.txt文件中,添加以下行:
   ```
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0")
   ```
   这将禁用编译器优化,可能会解决panic问题。

3. **修改uart.c文件**:如果问题仍然存在,您可以尝试修改`espesp-idfcomponentsdriveruart.c`文件中的`uart_rx_intr_handler_default`函数。在执行`uart_hal_is_tx_idle`宏之前,添加以下代码:
   ```
   portENTER_CRITICAL_ISR(&spinlock);
   // 执行uart_hal_is_tx_idle宏
   portEXIT_CRITICAL_ISR(&spinlock);
   ```
   这样可以确保在执行宏时,中断被临时禁用,从而避免panic。

4. **报告问题**:如果您认为这是一个编译器优化bug,建议您在ESP-IDF的GitHub仓库(https://github.com/espressif/esp-idf/issues)上报告这个问题。提供详细的错误分析过程和您的开发环境信息,以便开发者能够更好地了解问题并提供解决方案。


举报

更多回帖

发帖
×
20
完善资料,
赚取积分