需求

如何使用 stm32cubeide 开启 threadx 中的 stack check ?

解决

使能 stack check

  • 在项目属性中, C/C++ Build -> Settings -> MCU GCC Compiler ->

Preprocessor 中添加 TX_ENABLE_STACK_CHECKING

  • MCU G++ Compiler 中添加同样的 TX_ENABLE_STACK_CHECKING

printf 函数

#include "stdio.h"
#include "usart.h"

int _write(int file, char *ptr, int len)
{
      HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, 0xFFFF);
      return len;
}

*注意: 需要在头文件中增加 int _write(int file, char *ptr, int len); , 否则可能无法覆盖由 __attribute__((weak)) 修饰的原来 _write. *

处理函数

VOID StackErrorHandler(TX_THREAD * thread_ptr)
{
    printf("=============================================================== \n");
    printf("如下任务被检测出栈溢出 \n");
    printf("=============================================================== \n");
    printf(" 任务优先级 任务栈大小 当前使用栈  最大栈使用   任务名 \n");
    printf("   Prio     StackSize   CurStack    MaxStack   Taskname \n");

      TX_THREAD *p_tcb; /* 定义一个任务控制块指针 */

      p_tcb = &ServiceVibration;

      /* 遍历任务控制列表TCB list),打印所有的任务的优先级和名称 */
      do {
	      if (p_tcb != (TX_THREAD*) thread_ptr) {
		      p_tcb = p_tcb->tx_thread_created_next;
	      } else {
		      //Log::printf(level, "   %2d        %5d      %5d       %5d      %s\n",
		      printf("   %2d        %5ld      %5d       %5d      %s\n",
				      p_tcb->tx_thread_priority, p_tcb->tx_thread_stack_size,
				      (int) p_tcb->tx_thread_stack_end
						      - (int) p_tcb->tx_thread_stack_ptr,
				      (int) p_tcb->tx_thread_stack_end
						      - (int) p_tcb->tx_thread_stack_highest_ptr,
				      p_tcb->tx_thread_name);

		      while (1);
	      }
      } while (1);
}

绑定处理函数

app_azure_rtos.c 中的 tx_application_define 中,在申请字节池之后,增加 tx_thread_stack_error_notify(StackErrorHandler);

调试

这个调试比较有难度,如果直接在任务中,使用 int buf[5000] 这样的形式,很有可能直接就进 hard fault 了。所以需要重新思考方法。

经过多次调试发现以下信息:

  • 任务堆栈 app_main_stack_ptr 的值末尾是 0x0AAC, 因为堆栈大小是 1024,所以实际的堆栈范围是 0x0AAC0x0EAC 之间。并且在任务调用的时候,堆栈指针是从高地址向低地址使用,所以先从 0x0EAB 开始,逐渐向下开始 push, pop
  • 在任务中, int buf[5000] 这样的数组,比任务堆栈大太多了,会导致在调用任务的函数时,直接 hard fault.
  • 在任务中, int buf[100] 这样的数组,如果不使用的时候,当前堆栈指针为 0x0E58, 那么当使用的时候,会从 0x0E58 向下获取 100 个字,所以数组首地址是 0x0ECC8, 即数组占据了 0x0ECC80x0E58 这一段内存空间。

经过上面的这些信息,得出的可行方案是:

  1. 先获取当前的任务堆栈指针指向的内存地址,然后向下进行写入,最终写超出任务堆栈空间。
  2. 并且考虑到只有在任务切换的时候,才会检查堆栈溢出。所以每写入一次,就休眠一下,让任务进行调度。

具体测试代码如下:


int value;
int * buf = &value;
for (int i = 0; i < 1024; i++) {
    int * p = buf - i;
    *p = i;
    tx_thread_sleep(i + 10);
}

参考