当前位置: 首页 > news >正文

spsr 的恢复出错,导致 thumb 指令集的 it 条件运行指令运行异常,清晰的调试思路帮助快速解决问题

记一次调试过程

这是一个在 arm 架构上的 RTOS 上的调试过程。问题现象为使用 thumb 指令集的 libgcc 库的情况下,浮点运算随机出错。经过一番追踪调试,逐步缩小问题范围,最后定位问题,成功解决。

场景

在某款的国产 RTOS 上,由于客户应用需要,使用了thumb 指令集编译的 libgcc 的库,导致了同时运行了 arm 指令集和 thumb 指令集的代码。原本的 RTOS 未设计运行 thumb 指令的代码,也没有严格测试过,所以本次暴露了浮点运算偶尔出错的问题。

测试代码

客户精简过的暴露问题代码如下

unsigned long long   ulTa = 0x100;
unsigned long long   ulTb = 0x2001;
double dRea, dReb;
double dRes;while (1) {= (double)ulTa;dReb = (double)ulTb;dRes = dRea / dReb;if ((dRea == 0.0) || (dReab == 0.0) || (c <= 0.01f)) {printf("error!!\r\n");}}

这段代码的逻辑是强转了两个 unsigned long long 类型,然后进行除法运行,最后判断各个变量的结果是否符合预期,否则就报 error。

问题分析

抓取问题中的关键点:

  1. 和浮点运算有关,是不是我们的RTOS对于浮点运算的寄存器没有处理好
  2. 简单的代码反复运行只是偶尔出问题,而不是固定每次出问题,那么考虑其他外部的影响,首先考虑的是中断。

问题调试

bug调试的指导思想是,仔细观察问题剖析问题,提出怀疑一些原因,并修改代码验证

步骤1 剖析问题

靠近问题的实质,分析问题:既然计算出错,那么看看出错的时候的变量是什么样的。经过一些调试打印发现 3 个 double 类型的变量都出错过。出错的时候,打印这些变量对应地址存储的值,发现这些内存的值有 0 存在。因为 double 变量使用 8 bytes 的空间存放的,这些空间里存放的不是实际值,而是分符号位,指数位等。
根据这个现象,我有理由怀疑是某些时候的强转没有成功。

步骤2 证明和中断的相关性

进行关中断测试,在关闭中断的情况下,长时间跑了测试,时间足够长,发现并没有出错。那么证明的确和中断有关系。

步骤3 证明测试代码的逻辑是没有问题

排查其他问题:使用 arm 指令集的 libgcc 进行测试,使用同样的代码,时间足够长,也没有出错。那么证明测试代码的逻辑是没有问题的。

陷入了第一个瓶颈

确定中断有问题,那么推测偶尔出问题肯定是恰好某个时刻的中断踩到了特殊位置。修改代码关注是否是中断返回立即出错,打印分析被中断打断处的指令,观察是否是固定的某个指令。经过漫长的调试,观察到的确有规律。
分析长时间的出错前的 PC 指针的位置,很多是靠近 thumb 指令集里面的 IT block 指令。必须敏锐地抓住这个点。类似下面的 it eq 指令。

001007d4 <__aeabi_ul2d>:1007d4:	ea50 0201 	orrs.w	r2, r0, r11007d8:	bf08      	it	eq1007da:	4770      	bxeq	lr1007dc:	b530      	push	{r4, r5, lr}1007de:	f04f 0500 	mov.w	r5, #01007e2:	e00a      	b.n	1007fa <__aeabi_l2d+0x16>

简化问题

看手册学习 it block 指令。
// 学习过程不表

证明是 it 指令的问题

调试指导思想是:缩小问题的范围
想办法直接运行这个指令,观察是否出错。我使用内联汇编实现,使用伪代码解释代码逻辑

r1 = 0x10;
r2 = 0x10;
r3 = 0x20;
r4 = 0x40;r4 = r4 - r2;  //运行后 r4 = 0x30// itte ge , 上一行指令会改变 cpsr 的 flag 标志位,
// 若 ge: r1 = r1 + (r4 << 1)  //运行后 r1 = 0x70;
// 若 ge: r2 = r2 + 0x10;
// 若 lt: r4 = r4 + 0x10;

代码如下:

__attribute__((target("thumb"))) void thumb_ins(void);
void thumb_ins(void) {int ia = 0;int ib = 0;int ic = 0;int  cnt = 0;while (1) {ia = 0;ib = 0;ic = 0;asm volatile ("mov r1, #0x10");asm volatile ("mov r3, #0x20");asm volatile ("mov r2, #0x10");asm volatile ("mov r4, #0x40");asm volatile ("subs	r4, r4, r2");asm volatile ("itte  ge");asm volatile ("addge.w	r1, r1, r4, lsl #1");asm volatile ("addge r2, r2, #0x10");asm volatile ("addlt  r4, r4, #0x10");asm volatile ("mov %0, r1" : "=r" (ia));asm volatile ("mov %0, r2" : "=r" (ib));asm volatile ("mov %0, r4" : "=r" (ic));if ((ia != 0x70) || (ib != 0x20) || (ic != 0x30)) {printf("error!!!");printf(" r1 [0x%x] r2 [0x%x] r4[0x%x]\r\n", ia, ib, ic);}if (cnt ++ > 9000000) {printf(" r1 [0x%x] r2 [0x%x] r4[0x%x]\r\n", ia, ib, ic);cnt = 0;}}
}

经过运行测试,这个代码运行时,也会打印出 error;关闭中断进行测试,则运行不会打印 error。则完全证明了是 it 指令执行的问题。并且发现了出错时, it block 控制的条件允许代码全部没有执行,包括2条判断条件相反的指令都没有执行。

陷入了第二个瓶颈 分析 it 指令执行出错的原因

arm 手册上关于这个 it 指令从异常返回的描述:
On a branch or an exception return, if PSTATE.IT is set to a value that is not consistent with the instruction stream being branched to or returned to, then instruction execution is CONSTRAINED UNPREDICTABLE.
我们需要关注到异常返回的地址和异常返回时 PSTATE.IT 的标志状态。

  • 经过调试分析,已经确认我们的 RTOS 在异常保存上下文中 cpsr 的值是正确的。使用 msr 将其恢复也是正确的。也确认了我们RTOS配置的返回地址也是正确的。满足手册中提到的注意事项,问题陷入僵局。
  • 试图对比 Linux 源码中的异常保存和恢复过程中,对于 thumb 指令是否有特殊处理,然而并没有收获。

问题突破

经过艰难的调试之后,终于有了新的突破
我们知道,在异常返回时,是由硬件自动将 spsr 寄存器中的值恢复到 cpsr。我在设置PC跳转之前,读出 spsr 的值,保存到内存中,然后有了惊人的发现:此时 spsr 寄存器中的内容和上下文结构体中的值不一致,而且是关键的 IT block 的信息丢失。那么断定是恢复 spsr 的时候出错了。
经过简单调试,和 Linux 进行对比,发现了问题的根源。

问题解决

我们的 RTOS 恢复 spsr 使用的指令为 msr spsr, r1 ,编译生成的指令为 83ce0ae8: e169f001 msr SPSR_fc, r1。而 Linux 使用 msr spsr_cxsf, r1, 编译生成的指令为 83ce0ae8: e16ff001 msr SPSR_fsxc, r1。我的 RTOS 在恢复 spsr 中,丢失了关键的 s 和 x 域的内容:

  • c 指 CPSR中的control field ( PSR[7:0])
  • f 指 flag field (PSR[31:24])
  • x 指 extend field (PSR[15:8])
  • s 指 status field ( PSR[23:16])
    而恰好 IT block 信息存放在 [26:25] + [15:10]
    it block
IT[1:0], bits [26:25] 
IT block state bits for the T32 IT (If-Then) instruction. See IT[7:2] for explanation of this field.
IT[7:2], bits [15:10] 
IT block state bits for the T32 IT (If-Then) instruction. This field must be interpreted in two parts. • IT[7:5] holds the base condition for the IT block. The base condition is the top 3 bits of the 
condition code specified by the first condition field of the IT instruction.
• IT[4:0] encodes the size of the IT block, which is the number of instructions that are to be 
conditionally executed, by the position of the least significant 1 in this field. It also encodes the value of the least significant bit of the condition code for each instruction in the block.
The IT field is 0b00000000 when no IT block is active.

调试经验总结

  1. 分析问题,缩小问题范围
  2. 敏锐观察,抓住灵感
  3. 验证寄存器的值是否正确不能读中间状态,必须紧贴使用之前进行验证
  4. 相信硬件,在计算机的世界没有上帝

相关文章:

spsr 的恢复出错,导致 thumb 指令集的 it 条件运行指令运行异常,清晰的调试思路帮助快速解决问题

记一次调试过程 这是一个在 arm 架构上的 RTOS 上的调试过程。问题现象为使用 thumb 指令集的 libgcc 库的情况下&#xff0c;浮点运算随机出错。经过一番追踪调试&#xff0c;逐步缩小问题范围&#xff0c;最后定位问题&#xff0c;成功解决。 场景 在某款的国产 RTOS 上&a…...

mysql binlog 如何区分db

binlog不是InnoDB存储引擎特有的日志文件&#xff0c;是属于mysql server自己的日志文件。 提交事务的时候&#xff0c;同时会写入binlog 在MySQL中&#xff0c;Binary Log&#xff08;binlog&#xff09;记录了数据库更改操作的所有细节&#xff0c;对于实现数据复制、恢复以…...

ESP32 IDF linux下开发环境搭建

文章目录 介绍升级Python环境下载Python包配置编译环境及安装Python设置环境变量 ESPIDF环境搭建下载esp-idf 代码编译等待下载烧录成功查看串口打印 介绍 esp32 官方文档给的不是特别详细 参考多方资料 最后才完成开发 主要问题在于github下载的很慢本教程适用于ubuntu deban…...

光伏电站智能管理平台功能全面介绍

一、介绍 光伏电站智能管理平台专门为了光伏电站服务的融合了项目沟通、在线设计、施工管理、运维工单等多智能光伏管理系统&#xff0c;可以满足光伏电站建设前期沟通、中期建设和后续维护的一体化智能平台&#xff0c;同时通过组织架构对企业员工进行线上管理和数据同步&…...

SSL证书 购买流程

在购买SSL证书之前&#xff0c;需要知道一点相关的知识&#xff0c;通常包括以下几个环节&#xff1a; 一、确定需求 1、根据需要保护的域名数量&#xff0c;在以下三类中选择合适的证书类型&#xff1a; 单域名证书&#xff0c;只对一个域名&#xff08;例如abc.com&#x…...

C++|二叉搜索树

一、二叉搜索树的概念 二叉搜索树又称为二叉排序树&#xff0c;它或者是一颗空树&#xff0c;或者是具有以下性质的二叉树&#xff1a; 若它的左子树不为空&#xff0c;则左子树上所有节点的值小于根节点的值若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根结…...

网页html版面分析-- BeauifulSoup(python 文档解析提取)

介绍 BeauifulSoup 是一个可以从HTML或XML 文件中提取数据的python库&#xff1b;它能通过转换器实现惯用的文档导航、查找、修改文档的方式。 BeauifulSoup是一个基于re开发的解析库&#xff0c;可以提供一些强大的解析功能&#xff1b;使用BeauifulSoup 能够提高提取数据的效…...

第五十八节 Java设计模式 - 适配器模式

Java设计模式 - 适配器模式 我们在现实生活中使用适配器很多。例如&#xff0c;我们使用存储卡适配器连接存储卡和计算机&#xff0c;因为计算机仅支持一种类型的存储卡&#xff0c;并且我们的卡与计算机不兼容。 适配器是两个不兼容实体之间的转换器。适配器模式是一种结构模…...

程序员的归宿。。

大家好&#xff0c;我是瑶琴呀。 相信每个进入职场的人都考虑过自己的职业生涯规划&#xff0c;在不同的年龄段可能面临不同挑战&#xff0c;这点对于 35 的人应该更为感同身受。 对于程序员来说&#xff0c;大部分人的职业道路主要是下面三种&#xff1a;第一条&#xff0c;…...

ROS服务器通信

目录 一、角色 二、流程 注意 三、例子描述 四、srv文件 编译配置文件 vscode配置 五、Server.cpp编写例子 编写CMakeList 六、观察server的效果 七、Client编写例子 编写CMakeList 八、观察Client的结果 九、Client优化&#xff08;动态输入&#xff09; 了解argc…...

双向带头循环链表(图解)

文章目录 头节点(哨兵位)双向循环结构头插尾插头删尾删在指定位置之前插入数据删除指定位置之前的数据销毁链表 全部代码结语 单链表地址 头节点(哨兵位) 什么是头节点呢?头节点也叫哨兵节点,他在链表中进行不了任何操作,只是用来放哨用的,在单链表中我们当我们尾插的时候我们…...

富文本编辑器 iOS

https://gitee.com/klkxxy/WGEditor-mobile#wgeditor-mobile 采用iOS系统浏览器做的一款富文本编辑器工具。 原理就是使用WKWebView加载一个本地的一个html文件&#xff0c;从而达到编辑器功能的效果&#xff01; 由于浏览器的一些特性等&#xff0c;富文本编辑器手机端很难做…...

【OceanBase诊断调优】—— checksum error ret=-4103 问题排查

适用版本 OceanBase 数据库所有版本。 什么是 checksum data checksum&#xff1a;一个 SSTable 中所有宏块内存二进制计算出来的 checksum 值。反映了宏块中的数据和数据分布情况。如果宏块中数据一致但是数据分布不一致&#xff0c;计算出来的 checksum 也不相等。 column…...

融合Transformer与CNN,实现各任务性能巅峰,可训练参数减少80%

论文er看过来&#xff0c;今天给各位推荐一个热门创新方向&#xff1a;CNNTransformer。 众所周知&#xff0c;CNN通过多层卷积自动学习空间层级特征&#xff0c;能够有效提取图像局部特征。而Transformer通过自注意力机制全局建模&#xff0c;能够有效处理长距离依赖关系。 …...

K8s 多租户管理

一、K8s 多租户管理 多租户是指在同一集群中隔离多个用户或团队&#xff0c;以避免他们之间的资源冲突和误操作。在K8s中&#xff0c;多租户管理的核心目标是在保证安全性的同时&#xff0c;提高资源利用率和运营效率。 在K8s中&#xff0c;该操作可以通过命名空间&#xff0…...

Java面试题:Synchronized和Lock的对比

Synchronized和Lock对比 语法层面 Synchronized是关键字,源码在jvm中,用c语言实现 使用时,退出同步代码块时会自动释放 Lock是接口,源码由jdk提供,用java语言实现 使用时,需要手动调用unlock方法进行释放 功能层面 都属于悲观锁,具备基本的互斥,同步,锁重入功能 但Lock…...

VPN方案和特点

VPN方案和特点 VPN&#xff0c;或者称为虚拟专用网络&#xff0c;是一种保护你的在线安全和隐私的技术。它可以创建一个加密的连接&#xff0c;使你的在线活动对其他人不可见。以下是一些常见的VPN协议和它们的特点&#xff1a; 开放VPN (OpenVPN)&#xff1a;这是一种极为可…...

力扣HOT100 - 84. 柱状图中最大的矩形

解题思路&#xff1a; 单调栈 对于一个高度height[ i ]&#xff0c;找左右两边均严格小于它的值。 class Solution {public int largestRectangleArea(int[] heights) {int n heights.length;int[] left new int[n];int[] right new int[n];Deque<Integer> mono_st…...

【吃透Java手写】3-SpringBoot-简易版-源码解析

【吃透Java手写】SpringBoot-简易版-源码解析 1 SpringbootDemo2 准备工作2.1 Springboot-my2.1.1 依赖2.1.2 SpringBootApplication2.1.3 SJBSpringApplication2.1.3.1 run方法 2.2 Springboot-user2.2.1 依赖2.2.2 UserController2.2.3 UserApplication 2.3 分析run方法的逻辑…...

maven mirrorOf的作用

在工作中遇到了一个问题导致依赖下载不了&#xff0c;最后发现是mirror的问题&#xff0c;决定好好去看一下mirror的配置&#xff0c;以及mirrorOf的作用&#xff0c;以前都是直接复制过来使用&#xff0c;看了之后才明白什么意思。 过程 如果你设置了镜像&#xff0c;镜像会匹…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...