揭秘难以复现Bug的解决之道:堆栈分析实战

目录

  • 引言
    • 友情提示
    • 难以复现的Bug之痛
  • 寄存器(SP、LR)详解
    • SP寄存器:堆栈的指路明灯
    • LR寄存器:函数调用与异常处理的桥梁
  • 问题分析与解决流程揭秘
    • 保存现场
    • 分析堆栈数据
      • 堆栈结构
      • 入栈顺序
  • 案例
    • J-Link工具
      • 常用命令
      • 保存RAM数据到本地
    • 分析栈基本信息
      • 分析栈结构
      • 分析入栈顺序
    • 分析栈数据
  • 结语
  • 文章来源

引言

友情提示

本文接近三千字,预计阅读时间为10-15分钟。建议您在空闲时细细阅读,享受阅读的乐趣。

难以复现的Bug之痛

你是否曾为那些难以复现的Bug而头疼不已?本文将揭秘一种通过堆栈分析来定位并解决这类问题的神奇方法。

作为一名开发人员,在开发过程中会碰到各式各样的问题,如果能通过一些操作复现问题的,通过对目标板进行调试还能够逐步分析。

但是,如果由于某些原因不能对目标板进行调试,这种情况分析可就比较复杂了。

这不,前一阵子就碰到了一个问题,在调试过程中,怎样都无法复现问题。直到后来才发现一个现象,只有在对目标板上电时才有一定几率复现问题,即频繁的对目标板进行断电-上电测试。

降臣:“你难道不好奇你的功力是从何而来嘛?”
李星云:“不好奇”
降臣:“你难道不好奇为何换心之后 无法压制这突增的内力吗?”
李星云:“不好奇”
降臣:“你难道不好奇为何你只能活半年?”
李星云:“不好奇”
降臣:“你好奇”
李星云:“不好奇”
降臣:“你好奇”
李星云:“不好奇”

如何对这种问题进行分析,你难道不好奇嘛?

寄存器(SP、LR)详解

这里介绍两个寄存器。SP(Stack Pointer)寄存器和LR(Link Register)寄存器是非常重要的寄存器。

SP寄存器:堆栈的指路明灯

SP寄存器用于指向当前堆栈的栈顶位置。在函数调用时,SP寄存器会调整以反映堆栈的变化,确保数据正确地存储和取出。

LR寄存器:函数调用与异常处理的桥梁

LR寄存器有两个作用。

  1. 函数调用:当一个函数调用另一个函数时,LR寄存器保存当前函数的返回地址。
  2. 异常处理:当发生异常时,LR寄存器保存异常处理程序的返回地址。

问题分析与解决流程揭秘

保存现场

在处理难以复现的Bug时,保存现场数据是至关重要的一步。这就像是在犯罪现场拍照留证一样,能够为我们后续的分析提供宝贵的线索。

当上电出现死机问题后,插上Jlink,使用J-Link commander软件连接目标板,然后通过命令将芯片RAM中的区域保存成二进制文件存放到电脑本地。

如果应用程序的版本号不确定,也可以将ROM中的程序保存到本地,操作方法同保存RAM,后面会说到如何保存。

接下来,我们会分析RAM中的数据。

幸运的情况下,可以直接找到最后一次被调用的函数,然后分析某个函数的功能,即可找到问题。而有的情况下就可能还需要根据函数的前后调用关系分析出问题所在。

分析堆栈数据

我之前也不知道如何分析堆栈数据,第一次分析的时候就感觉进了一个迷宫,绕着绕着就把自己绕进去了。

你可以想象一下,拿着一份二进制文件,去分析函数的调用关系,想想就脑壳疼…

分析堆栈时需要知道下面几个知识点,才能正确分析,我接下来会解释一下。

堆栈结构

堆栈结构主要有四种类型,分别是满递减堆栈、满递增堆栈、空递减堆栈和空递增堆栈。

递增/递减是指栈向高地址还是低地址增长,满是指栈指针(SP)总是指向堆栈中的最后一个元素,即最后压入的数据。空是指栈指针(SP)总是指向下一个将要放入数据的空位置。

常用的可能还是满递减堆栈比较多一点。

入栈顺序

因为我们要分析栈中的数据,所以我们通过汇编查看依次有哪些数据入栈,然后分析出当前的LR寄存器中的值。
例如下方是一个入栈的汇编指令。我们需要知道入栈的顺序,是从右往左入栈的还是从左往右入栈的。

0x08000644 B570      PUSH     {r4-r6,lr}

案例

J-Link Commande工具

首先需要安装J-Link软件,去官网https://www.segger.com/downloads/jlink/下载,这里是一个套件,安装后会有若干个独立的小软件。

我们需要使用的是J-Link Commande软件。打开软件之后,可以输入?来查看支持的命令,如下图:
在这里插入图片描述
感兴趣的可以研究这些命令,会对自己有所帮助的。

分析栈基本信息

这里需要分析的是栈属于上面说的四种结构的哪一种,以及数据入栈的顺序是如何的。

这里我们需要将ARM仿真器连接目标板进行调试,通过单步调试定位到PUSH汇编指令中,如下图所示:
在这里插入图片描述
当未执行 0x08000E0C B510 PUSH {r4,1r} 入栈操作时,当前的栈指针SP指向0x20000658地址,并且该地址中值不为空。从而说明当前的SP指向的是最后一个入栈的元素,即可判定为堆栈。

随后我们单步执行,让其执行入栈操作,再来看堆栈中的数据,如下图所示:
在这里插入图片描述
此时我们看到SP的地址为0x20000650,执行了入栈操作,SP的地址减小了,从而判定堆栈的生长方向是递减的。根据上述两个判断从而能得出堆栈结构为满递减堆栈

接下来我们要判断数据入栈的顺序,这个汇编是将r4以及lr寄存器中的值入栈。分析入栈后的数据得知,第一个入栈的数据为0x08000DE1,刚好是lr寄存器中的值。我在图片中也标注了入栈数据对应的寄存器。从而可以得出结论,入栈的顺序是从右往左的,先入栈lr后入栈r4

保存RAM数据到本地

需要执行如下几个步骤,即可连接到目标板。

  1. 当系统死机后,需要将目标板连接到ARM仿真器。
  2. 使用管理员的身份打开J-Link Commande工具(否则后面保存数据会提示写入文件失败)
  • connect命令准备连接目标板
  • 选择目标芯片型号
  • 选择调试接口
    • JTAG
    • SWD
    • cJTAG
  • 选择接口速度
  • 连接成功提示
  1. SaveBin命令保存栈数据
  • SaveBin c:/ram.bin 0x20000000 0x5000
  • 我这里是将整个RAM区域的数据保存起来了

这里用到了一个SaveBin的命令,命令的原型如下:

SaveBin  SaveBin <filename>, <addr>, <NumBytes>  Save target memory range into binary file.

如下图所示,是我连接目标板到保存RAM数据的所有操作,此时C盘根目录就会出现一个ram.bin的文件。
在这里插入图片描述## 分析栈数据

当我们使用jlink连接目标板后,输入命令h可以看到一些关键信息,如下图:
在这里插入图片描述
可以看到SP(R13)= 20000654, PC = 08000E1E, IPSR = 000 (NoException),那我们先去看pc指向的地址是属于哪一个函数的。我们可以直接在汇编窗口中输入地址然后直接跳转过去,如下图所示:
在这里插入图片描述
通过定位得知,这个地址是在InitC函数内,如下图所示:
在这里插入图片描述而且这个函数刚执行的时候,执行了一次PUSH操作入栈了三个元素,根据之前的分析,入栈的顺序是从右往左,所以第一个入栈的数据就是LR,又因为当前的SP指针指向的地址为20000654,然后去查ram.bin数据,如下图
在这里插入图片描述
从而可以推断出LR的值为0x08000E13,这里是PC+1的值,所以函数返回的实际地址为0x08000E12。然后再在跳转到这个地址,根据上面图片,发现是一个出栈三个元素的指令,同样找到LR实际地址0x08000E02然后在跳转到这个地址,反发仍然是一个出栈三个元素的出栈指令,同样找到LR实际地址为0x08001046,再继续跳转到这个地址,发现是一个延时函数了如下图:
在这里插入图片描述
至此,就是通过分析栈数据分析函数的调用关系,这里写了一个简单的测试历程,通过按下按键会执行几层函数调用最后进入一个死循环,从而模拟死机的情况。

怎么样,看着是不是感觉特简单,但是在实际的开发过程中,真实情况可能比这复杂百倍。

结语

为了写这篇文章真的下了血本,我买了一个STM32的小开发板以及一个ARM仿真器,这对于原本就不富裕的我来说无疑是雪上加霜。

希望这篇文章能帮助你解决实际开发中的问题,也欢迎分享你的经验和心得。

感谢您的耐心阅读!如果您觉得本文有帮助,请不要吝啬您的点赞、分享和收藏!

文章来源

公众号:typedef
揭秘难以复现Bug的解决之道:堆栈分析实战

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全国大学生数据建模比赛——深度学习

全国大学生数学建模比赛中&#xff0c;深度学习可以成为解决复杂问题的有力手段。 一、深度学习的优势在比赛中的体现 强大的模式识别能力&#xff1a;深度学习模型&#xff0c;如卷积神经网络&#xff08;CNN&#xff09;和循环神经网络&#xff08;RNN&#xff09;&#xff0…

如何在 Raspberry Pi 5 上设置 Raspberry Pi AI Kit

本指南将帮助您在 Raspberry Pi 5 上安装 Raspberry Pi AI Kit。这将使您能够使用 Hailo AI 神经网络加速器运行 rpicam-apps 摄像头演示。 如果您在开始安装人工智能套件之前需要帮助&#xff0c;本指南提供了安装过程的分步图片。 安装人工智能套件&#xff1a;https://www.…

【初出江湖】SOA 与微服务:哪个最适合您的业务?

目录标题 面向服务的体系结构 (SOA)SOA 角色那么它们是如何通信和协同工作的呢&#xff1f; SOA 的优势 微服务架构微服务的优势 SOA 和微服务之间的区别SOA 与微服务&#xff1a;常见问题采用 SOA 和微服务面临哪些挑战&#xff1f;SOA 和微服务是否可以共存&#xff1f;每种体…

wpf prism 《1》、区域 、模块化

安装prism.DryIoc 修改app.xaml <prism:PrismApplication x:Class"WpfApp3.App"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local"clr-namespace:W…

vue2的使用

准备容器引包&#xff08;官网&#xff09; — 开发版本/生产版本创建Vue实例 new Vue()指定配置项&#xff0c;渲染数据 el:指定挂载点data提供数据 可以把线上的cdn的vue的地址下载到本地进行依赖。 Vue2下载地址 https://v2.vuejs.org/v2/guide/installation.html vue.js没…

Linux虚拟机搭建K8S环境

文章目录 一、环境准备二、系统初始化三、部署master四、添加node节点五、部署网络六、部署dashboard七、登录dashboard面板 一、环境准备 首先在vmware上新建4台相同配置的虚拟机&#xff0c;除了IP和主机名外&#xff0c;其余配置相同。由于是搭建K8S初始环境&#xff0c;没…

开学寄快递,行李轻松寄,出行无压力

“春风得意马蹄疾&#xff0c;一日看尽长安花。”新的学期&#xff0c;新的征程&#xff0c;新生们在准备迎接开学时&#xff0c;不想拖着重重的行李开学的&#xff0c;那么寄快递这件事可得提上日程啦! 一、如何选择快递 如何选择一家价格实惠的快递公司成为了大家关注的焦点…

Linux bash脚本 批量创建文件

目录 一. 需求二. 前置知识一. 无进度条版本1.1 知识点1.2 代码1.3 效果 二. 有进度条版本2.1 代码2.2 效果 一. 需求 在当前目录下生成指定年份的文件&#xff0c;要求从生成1月到12月&#xff0c;每个月份的文件。 若用户不指定年份&#xff0c;则默认生成当前年的文件提示…

【C++】手动实现String类的封装(分文件编译)

实现了String类的大部分封装&#xff0c;采用分文件编译 //mystring.h #ifndef MYSTRING_H #define MYSTRING_H#include <iostream> #include <cstring> using namespace std;class myString { private:char *str; //定义一个字符串int size; //记录字符串…

[知识分享]华为铁三角工作法

在通信技术领域&#xff0c;尤其是无线通信和物联网领域&#xff0c;“华为铁三角”是华为公司内部的一种销售、交付和服务一体化的运作模式。这种模式强调的是以客户为中心&#xff0c;通过市场、销售、交付和服务三个关键环节的紧密协作&#xff0c;快速响应客户需求&#xf…

tensorrt plugin

自定义plugin 流程 首先明确要开发的算子&#xff0c;最好是 CUDA 实现&#xff1b;继承 IPluginV2DynamicExt / IPluginV2IOExt类实现一个Plugin 类&#xff0c;在这里调用前面实现的算子&#xff1b;继承 IPluginCreator 类实现一个 PluginCreator 类&#xff0c;用于创建插…

JeecgBoot积木报表AviatorScript表达式注入漏洞复现

文章目录 漏洞信息漏洞复现环境搭建poc复现DNSLog验证 漏洞信息 影响组件&#xff1a;JimuReport积木报表 影响版本&#xff1a;v1.6.0 &#xff1c; JimuReport ≤ 1.7.8 漏洞名称&#xff1a;AviatorScript表达式注入漏洞 漏洞链接&#xff1a;积木报表软件存在AviatorSc…

redis分布式是如何实现的(面试版)

需要结合项目中的业务进行回答&#xff0c;通常情况下&#xff0c;分布式锁使用的场景&#xff1a;集群情况下的定时任务、抢单、幂等性场景。 下面先来看一个抢卷场景&#xff1a; 以下情况会出现超卖情况&#xff1a; 因为线程会交替执行&#xff0c;所以线程查询优惠价的数…

在大语言模型中,生成文本的退出机制,受max_generate_tokens限制,并不是所有的问答都完整的跑完整个transformer模型

目录 在大语言模型中,生成文本的退出机制,受max_generate_tokens限制,并不是所有的问答都完整的跑完整个transformer模型 1. max_generate_tokens的作用 2. 退出机制与Transformer模型 3. 实际应用中的影响 4. 结论 在大语言模型中,生成文本的退出机制,受max_genera…

php法律事务综合管理系统Java律师事务所业务流程管理平台python法律服务与案件管理系统(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

【解决方案】项目重构之如何使用 MySQL 替换原来的 MongoDB

前言 在笔者 Java 后端开发的项目经历中&#xff0c;MySQL 和 MongoDB 都有使用过作为后端的数据库来对业务数据进行持久化&#xff0c;两者没有孰优孰劣之分&#xff0c;都可以在合适的场景下发挥出它们的优势。 今天要分享的是一个项目重构过程中如何将数据库选型由原来的 Mo…

“线程池中线程异常后:销毁还是复用?”

目录 一、验证execute提交线程池中 测试 结论 二、验证submit提交线程池中 测试 结论 三、源码解析 查看submit方法的执行逻辑 查看execute方法的执行逻辑 为什么submit方法&#xff0c;没有创建新的线程&#xff0c;而是继续复用原线程&#xff1f; 四、总结 需要说…

【UML建模】时序图的那点事

【UML建模】时序图的那点事 开篇词&#xff1a;干货篇&#xff1a;1.概述2.时序图的组成元素2.1角色&#xff08;Actor&#xff09;&#xff1a;2.2生命线&#xff08;Lifeline&#xff09;&#xff1a;2.3消息&#xff08;Message&#xff09;&#xff1a;2.4激活条&#xff0…

【Day07】

目录 MySQL-DQL- 基本查询 MySQL-DQL- 条件查询 MySQL-DQL- 聚合函数 MySQL-DQL- 分组查询 MySQL-DQL- 排序查询 MySQL-DQL- 分页查询 MySQL-DQL- 案例 MySQL-多表设计-一对多 MySQL-多表设计-一对多-外键约束 MySQL-多表设计-一对一&多对多 MySQL-多表设计-案例…

学习日志8.30--防火墙NAT

目录 一、实验环境配置 二、配置防火墙静态NAT一对一 三、配置防火墙静态NAT多对多 四、配置防火墙NAT端口转换NAPT 五、防火墙smart-nat、easyip 六、防火墙三元组NAT 在学习过基于路由器的NAT网络地址转换&#xff0c;现在学习基于防火墙NAT的网络地址转换&#xff0c;…