如何灵活运用keil工具进行问题分析(2)— 定位FreeRTOS的栈溢出导致hardfault问题

前言

(1)如果有嵌入式企业需要招聘湖南区域日常实习生,任何区域的暑假Linux驱动实习岗位,可C站直接私聊,或者邮件:zhangyixu02@gmail.com,此消息至2025年1月1日前均有效
(2)之前我发表过一篇关于如何利用keil工具解决程序卡死问题的博客。但是依旧有朋友遇到hardfault问题还是不能够灵活解决。其实hardfault问题和我之前说的程序卡死的排查流程基本是一样的。就只有细微的不同点。但是为了做保姆级教程,因此再写一篇博客赘述一下。

问题复现

(1)首先看一下网友的问题:
<1>他说程序运行不到1分钟就进入hardFault。这个代码量很少,所以能够很快定位到问题,但是如果程序大了,如何定位问题。
<2>他说可以尝试通过查看R0–R15寄存器是否可以找出问题。
(2)首先回答第一个问题,对于程序的问题定位,肯定是一个复杂而又漫长的过程,经常能够遇到一些玄学问题因此我们要学会掌握技巧,下面的方法就是技巧之一。其次是第二个问题,这明显就是没搞明白寄存器的作用分别是干嘛的,完全是胡乱看寄存器。
(3)问题如下:

在这里插入图片描述

排查流程

栈回溯分析

(1)既然知道了问题是什么了,那么就开始排查,根据前面的介绍,我们可以通过keildebug工具中的Call Stack进行栈回溯。分析程序卡死之前被哪些函数调用,然后逐步分析这些函数可能的问题。
(2)但是,不幸的是,我们会发现Call Stack中只有一个HardFault_Handler。那么明显说明Call Stack工具现在是用不了的。

在这里插入图片描述

CM3和CM4异常返回值

(1)之前那篇博客,是介绍的普通的程序卡死如何进行排查,然后栈回溯调试。而这里有些许的不同,在于是hardfault错误,因此只有这个地方会不同。
(2)前文我介绍了,程序卡死一般是看R14(LR)寄存器,因为这个寄存器存放返回值信息,通俗来说就是C语言的return根据这个寄存器进行函数返回。
注:下图摘抄自CM3权威指南3.1寄存器组章节

在这里插入图片描述

(3)那么现在我们看一下R14(LR)寄存器值是什么。我们会发现里面居然是0xFFFFFFFD!很明显,我函数返回不可能是返回到一个0xFFFFFFFD,因为程序一般都是存储在0x08开头的位置。那么这个时候我们就需要来了解一下CM3CM4的异常返回值的知识了。

在这里插入图片描述

(4)我们在看上面的寄存器介绍的时候,有没有发现一个问题,R13怎么有两个寄存器,一个MSP,一个是PSP?因为MSP用于内核和异常处理,而PSP用于进程的堆栈。这使得Cortex-M3处理器可以轻松实现多任务操作系统。
(5)上述这些,如果想详细了解的个人建议直接看这篇博客,介绍的非常的好:RTOS系列文章(6):Cortex-M3/4之SP,MSP,PSP,Thread模式、Handler模式、内核态、用户态
(6)但是,这部分知识跟我们问题定位关系不大。我们只需要了解,如果R14(LR)如果是0xFFFFFFFD,那么就看PSP寄存器。如果R14(LR)0xFFFFFFF9,那么就看MSP寄存器。
注:下图摘抄自CM3权威指南9.6异常返回值章节

在这里插入图片描述

手动栈回溯

(1)很好,既然我们有上述知识了之后。因为R14(LR),我们就知道现在要看PSP寄存器的值了,之后内存信息知道堆栈数据,然后手动栈回溯了解HardFault_Handler之前是卡死在哪里。
(2)现在我们看PSP寄存器,知道堆栈寄存器存储的是0x00000020。那么就打开Memory工具,查看当前芯片内部的存储信息。
这里需要注意,PSP的堆栈寄存器,不是PC或者LR寄存器,因此是看的Memory工具而不是Disassembly工具。

在这里插入图片描述

(3)根据堆栈指针寄存器,我们知道了当前堆栈指向位置存储的信息。那么如何根据这些信息进行问题的排查呢?这个时候我们就需要了解一下CM3/CM4的中断/异常的入栈知识了。CM3/CM4的中断响应时候,硬件是会自动进行入栈的,他的栈存储位置如下。
注:下图摘抄自CM3权威指南9.1.1入栈章节

在这里插入图片描述

(4)之后我们就可以根据上面这个知识点,进行分析。可以得知,如果程序现在正常运行,那么应该是运行到0x08000145位置。

在这里插入图片描述

(5)于是我们根据0x08000145找到程序实际卡死的地方。发现是卡死在port.c文件中,而这个文件又是FreeRTOS的官方源码,出错概率很低。

在这里插入图片描述

解决思路

定位可能的原因

(1)有了上述的分析,其实问题就已经解决一大半了。现在我们知道程序是卡死在0x08000145位置,于是我们可以在这个位置进行打断点。然后复位程序重新跑,此时Call Stack能够帮助我们进行栈回溯了。
注意:程序需要多按几次全速跑,一直等到进入HardFault_Handler的前一刻停止。因为我们不清楚到底是什么时候卡死在这个断点处的。

在这里插入图片描述

(2)然后我们就开始分析Call Stack的内容,发现除了TaskGenericFunction()其他的要么是FreeRTOS官方程序,要么是C库程序。于是我们直接看TaskGenericFunction()程序到底发生了什么。跳转过去之后,我们发现这个任务中只有一个printf()打印。我尝试逐步注释printf(),发现可以解决问题,然后就错误的认为这是和printf()线程安全有关导致的bug。但是后面发现,这并不问题的关键。

在这里插入图片描述

(3)如何发现真正的问题是什么?我们可以看TaskGenericFunction()函数的Location/Value,会发现这里居然是0x00000000!我们都知道STM32 的内存管理起始就是对0X0800 0000 开始的Flash部分和0x2000 0000 开始的SRAM部分使用管理。因此,这个地方毫无疑问是有问题的。出现这种问题,基本就可以知道是栈溢出的问题了。

在这里插入图片描述

FreeRTOS的uxTaskGetStackHighWaterMark()函数

(1)既然我们知道了是栈溢出的问题,那么如何知道是那个地方栈溢出了呢?此时就不得不是使用到FreeRTOSuxTaskGetStackHighWaterMark()函数了。这个函数可以监控任务使用的栈空间历史使用剩余值的最小值。使用方法如下:这里需要注意,需要在FreeRTOSConfig.h中将INCLUDE_uxTaskGetStackHighWaterMark设置为1,才可以使用这个函数。

/**
 * @brief   查看任务使用的栈空间大小
 *
 * @param   xTask 任务句柄
 *
 * @return 任务堆栈可用的最小值,单位word(4字节)
 */
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );

/* === 使用方法 === */
void StartCubemxTask(void *argument)
{
  /* USER CODE BEGIN StartCubemxTask */
	char *CubemxTaskPrintf = (char *)argument;
	UBaseType_t Cubemx_Stack;
  /* Infinite loop */
  for(;;)
  {
		printf(CubemxTaskPrintf);
		Cubemx_Stack = uxTaskGetStackHighWaterMark(keilTaskHandle);
		printf("CubemxTask is %ld\r\n",Cubemx_Stack);		
  }
  /* USER CODE END StartCubemxTask */
}

(2)了解了这个函数之后,那么我们尝试在每个任务中加入一个uxTaskGetStackHighWaterMark()监控栈使用情况,并且都打上断点。

在这里插入图片描述

(3)按照如下办法,之后开始查看他们的栈使用情况。

在这里插入图片描述

(4)我们一点一点的开始测试,最终发现是能够找到栈空间被榨干的地方了。

在这里插入图片描述

(5)现在我们思考一下为什么这个地方栈空间会被榨干。我们知道,起始是执行了下面这个for循环之后,栈空间就为空了。因此我们再看一下任务分配的时候任务栈空间是多少,能够发现是100 word,也就是400字节。而这里的buf[]就有500字节了。于是问题就成功发现了,此时我们可以尝试调小buf[]或者是增大任务栈空间。

for ( i = 0; i < 500; i++)
	buf[i] = 0;

在这里插入图片描述

总结

(1)ARM的M系列控制器hardfault问题调试步骤,应该基本都是这样的。所以说我们要多练习,程序出故障的可能性很多,栈溢出,逻辑错误,硬件bug都是有可能的。
(2)题外话:说实话,比起调程序bug,猜女孩子的心思才是最难的。硬件有问题,我有万用表,逻辑分析仪,示波器。软件问题,我有编译器,调试器,debug工具,栈回溯。女生发脾气,怎么也不知道到底哪里得罪姑奶奶了,最恶心的是bug不可复现。哭死

参考

(1)RTOS系列文章(6):Cortex-M3/4之SP,MSP,PSP,Thread模式、Handler模式、内核态、用户态
(2)STM32的内存管理相关(内存架构,内存管理,map文件分析)
(3)hardfault问题分析解决及记一次ucosIII环境下的hardfault解决

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/722976.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【会议征稿,JPCS出版】第四届测量控制与仪器仪表国际学术会议(MCAI 2024,7月19-21)

随着各行各业向智能化、自动化、信息化方向发展&#xff0c;对于精密测量与控制的需求日益增加。同时该专业人才在科学研究、制造业、互联网等领域就业前景也非常广阔。测控技术与仪器是现代工程与科技的重要领域之一&#xff0c;为各行各业提供精准、可靠的测量、控制和检测手…

LuckySheet导入报错file.match is not a function解决方法

最近在研究有什么类Excel的表格库。目前看到的话就是luckysheet和univer。 univer是luckysheet的升级版&#xff0c;问题是开源版没有提供导入导出功能。 在尝试luckysheet的导入功能的时候&#xff0c;会发现官方demo是没有报错的。而在自己的项目里就会有报错 导入呢是官方提…

jsp运行提示_jsp.java某行存在错误问题的解决

jsp运行提示XXX_jsp.java某行存在错误问题的解决 在编译运行jsp文件时&#xff0c;出现类似如下提示&#xff1a; 49行发生错误&#xff0c;要注意&#xff1a; 这里所指的49行并非jsp文件的第49行&#xff0c;而是编译后的jsp.java文件的第49行。 因此&#xff1a;解决问题…

FreeRTOS,使用SDIO外设会进入Hard FaultHandler

解决方法&#xff1a; 1.读写函数中&#xff0c;要使能所有中断。 2.读写缓冲数组为全局变量 3.任务堆栈开辟的大点

process is not defined

最近在开发项目中莫名遇到这个问题&#xff0c;网上查阅很多资料&#xff0c;上面的解决办法都不管用。最后在node_modules中找到path-browserify模块&#xff0c;修改index.js文件&#xff1a; 将process.cwd()注释&#xff0c;改为cwd __dirname,然后重启项目就好了。

教育与学习助手:Kompas AI革新学习体验

一、引言 在个性化学习需求日益增长的今天&#xff0c;教育领域正经历着一场技术革命。Kompas AI&#xff0c;作为一款先进的人工智能助手&#xff0c;正以其独特的功能和应用&#xff0c;为学习者提供个性化的学习支持&#xff0c;满足不同背景和需求的学生。 二、功能介绍 Ko…

kotlin区间

1、创建 fun main() {// 全闭区间val intRange 1..3 // int 区间val charRange a..c // 字符区间// 打印println(intRange.joinToString()) // 1,2,3println(charRange.joinToString()) // a,b,c// 左闭右开区间val intRangeExclusive 1 until 3// 倒叙全闭区间val intDown…

网络层 IP协议【计算机网络】【协议格式 || 分片 || 网段划分 || 子网掩码】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;前提 二&…

【NOI-题解】1431. 迷宫的第一条出路

文章目录 一、前言二、问题问题&#xff1a;1431. 迷宫的第一条出路 三、感谢 一、前言 二、问题 问题&#xff1a;1431. 迷宫的第一条出路 类型&#xff1a;深度搜索、回溯、路径打印 题目描述&#xff1a; 已知一 NN 的迷宫&#xff0c;允许往上、下、左、右四个方向行走…

路由框架 ARouter 原理及源码解析

文章目录 前言一、ARouter 简介二、ARouter 使用1.添加依赖和配置2.添加注解3.初始化SDK4.发起路由操作 三、ARouter 成员1. PostCard 明信片2. Interceptor 拦截器3. Warehouse 路由仓库4. ARouter 注解处理 四、ARouter 原理五、ARouter 源码分析1. ARouter 初始化1.1 ARoute…

类的继承性(Java)

本篇学习面向对象语言的第二特性——继承性。 1 .为什么需要继承 我们来举个例子&#xff1a;我们知道动物有很多种&#xff0c;是一个比较大的概念。在动物的种类中&#xff0c;我们熟悉的有猫(Cat)、狗(Dog)等动物&#xff0c;它们都有动物的一般特征&#xff08;比如能够吃…

leetcode498 对角线遍历

题目 给你一个大小为 m x n 的矩阵 mat &#xff0c;请以对角线遍历的顺序&#xff0c;用一个数组返回这个矩阵中的所有元素。 示例 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,4,7,5,3,6,8,9] 解析 本题目主要考察的就是模拟法&#xff0c;首…

【动态规划】斐波那契数列模型 {动态规划的解题思路;动态规划的优化方案;动态规划的基础题型}

一、经验总结 动态规划题型的解题思路&#xff1a; 状态表示&#xff1a;dp[i]的含义是什么。通过解题经验和题目要求得到&#xff0c;一般有以下两个方向&#xff1a; 以i位置为起点以i位置为终点 状态转移方程&#xff1a;dp[i]怎么求。根据距离i位置最近的一步划分问题&am…

公司面试题总结(六)

31.说一说 webpack 的构建流程是什么&#xff1f; ⚫ 初始化流程&#xff1a; ◼ 从配置文件和 Shell 语句中读取与合并参数 ◼ 初始化需要使用的插件和配置插件等执行环境所需要的参数 ⚫ 编译构建流程&#xff1a; ◼ 从 Entry 发出&#xff0c;针对每个 Module 串行…

【 ClickHouse】 ClickHouse数据类型(整型、浮点型、布尔型、Decimal型、字符串、数组、时间类型)(二)

ClickHouse数据类型 整型 固定长度的整型&#xff0c;包括有符号整型或无符号整型。 1)整型范围&#xff1a; Int8 - [-128 : 127] Int16 - [-32768 : 32767] Int32 - [-2147483648 : 2147483647] Int64 - [-9223372036854775808 : 9223372036854775807]2)无符号整型范围&…

【linux】内核从tcp层调用IP层摸索中

合入代码&#xff1a; 登录 - Gitee.com 这是运行日志&#xff1a; https://gitee.com/r77683962/linux-6.9.0/raw/master/test_log/kern_tcp_ip.log 日志截取部分&#xff08;也不知道对不对&#xff0c;凭感觉走。。。。&#xff09;

关机充电动画:流程与定制

关机充电动画&#xff1a;流程与定制 基于MTK平台Android 11分析 生成logo.bin 关机充电动画是由一系列的bmp图片组成的&#xff0c;这些图片资源存在于vendor/mediatek/proprietary/bootable/bootloader/lk/dev/logo目录下&#xff08;当然不仅保护关机充电动画&#xff0c…

自动驾驶仿真:Carsim转向传动比设置

文章目录 一、转向传动比概念二、设置转向传动比1、C factor概念2、Steer Kinematics概念3、传动比计算公式 三、转向传动比验证 一、转向传动比概念 转向传动比&#xff08;Steering Ratio&#xff09;表示方向盘转动角度与车轮转动角度之间的关系。公式如下&#xff1a; 转向…

乘法与位运算

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 参考代码 描述 题目描述&#xff1a; 进行一个运算单元的电路设计&#xff0c;A[7:0]*11111011&#xff0c;尽量用最少的资源实现&#xff0c;写出对应的 RTL 代码。 信号示意&#xff1a; A信号输入 B 信号…

[自动驾驶 SoC]-4 特斯拉FSD

FSD, 参考资料来源FSD Chip - Tesla - WikiChip 另外可参考笔者之前分享文章&#xff1a;[自动驾驶技术]-6 Tesla自动驾驶方案之硬件&#xff08;AI Day 2021&#xff09;&#xff0c;​​​​​​​[自动驾驶技术]-8 Tesla自动驾驶方案之硬件&#xff08;AI Day 2022&#xf…