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

news2024/9/22 3:38:18

目录

  • 引言
    • 友情提示
    • 难以复现的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.coloradmin.cn/o/2098865.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【最新华为OD机试E卷】最大报酬(100分)-多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,…

【Python】企业排名、地域分布与词云分析可视化

目录 数据处理 Pyecharts 各国数量 分布地图 数量占比 城市分布 营业收入 利润转化 数据处理 2021世界五百强企业数据&#xff0c;包含公司名称、公司链接、营业收入(百万美元)、利润(百万美元)、国家等信息。数据集下载&#xff1a;Python企业排名、地域分布与词云分…

opencv-python 图像增强十七:泊松图像融合

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、概述二&#xff0c;实现&#xff1a; 前言 在深入探讨图像处理与计算机视觉领域的过程中&#xff0c;我们不禁对图像融合技术的精妙与实用性感到着迷。图像…

物联网之云平台架构

一&#xff0c;一个典型的物联网云平台 一个典型的物联网&#xff08;IoT&#xff09;云平台需要实现多个功能&#xff0c;以支持物联网设备的接入、数据处理、设备管理、实时控制等需求。 &#xff08;一&#xff09;核心功能 1&#xff0c;设备接入与管理&#xff1a; - 设…

【达梦数据库】DBeaver连接达梦数据库

打开 DBeaver&#xff0c;新建驱动管理器 新建驱动管理器&#xff0c;配置信息如下 添加库文件&#xff0c;jar包使用项目上使用的jdbc驱动包即可&#xff0c;找到本地maven仓库jar位置进行添加。 <dependency><groupId>com.dameng</groupId><artifact…

打开配置好的gee的jupyter Lab环境

目录 打开anconda 打开箭头下的cmd环境&#xff0c;输入jupyter lab

Spring MVC 八股文

目录 重点 SpringMVC的工作原理 Spring MVC 拦截器 Spring MVC 的拦截器和 Filter 过滤器有什么差别&#xff1f; 基础 什么是SpringMVC SpringMVC的优点 Spring MVC的核心组件 Spring MVC的常用注解由有哪些 Controller 注解有什么用 重点 SpringMVC的工作原理 1、客…

人脸静态活体检测(高精度版) API 对接说明

人脸静态活体检测&#xff08;高精度版&#xff09; API 对接说明 本文将介绍人脸静态活体检测&#xff08;高精度版&#xff09;API 对接说明&#xff0c;它可用于对用户上传的静态图片进行防翻拍活体检测&#xff0c;以判断是否是翻拍图片。 接下来介绍下 人脸静态活体检测…

浅谈sizeof() 函数在Arduino中的使用

下面浅谈 sizeof() 函数在Arduino中的使用 注意&#xff1a;这里用sizeof(str[2])&#xff0c;sizeof(str[0])&#xff0c;sizeof(str[1])都是一样的 String str[6]{"abc","defg","hijk","lm","n"}; int num; void setup…

C++项目详细分析_WebServer

前言 项目地址 项目介绍 源码详细分析 项目路径如下&#xff1a; 1.webserver.cpp 头文件和构造函数 #include "webserver.h"WebServer::WebServer() {// http_conn类对象users new http_conn[MAX_FD];// root文件夹路径char server_path[200];getcwd(server…

【C++】N卡无法录制,如何下载C++

N卡无法录制&#xff0c;如何下载C C 官方下载路径&#xff1a; https://www.microsoft.com/zh-cn/download/details.aspx?id48145&134b2bb0-86c1-fe9f-d523-281faef416951&751be11f-ede8-5a0c-058c-2ee190a24fa6True 第一步 检查N卡驱动是不是最新版本 第二步 下载…

拓扑结构_替代SN6505推挽式低噪声隔离变压驱动器输出功率1-3W

PC6505 是一款专门为小体积、低待机功耗微功率隔离电源而设计的推挽式变压器驱动器&#xff0c;其外围只需匹配简单的输入输出滤波电容、隔离变压器和整流电路&#xff0c;即可实现 3.3V 或 5V 输入、3.3V~24V 输出、输出功率 1~3W 的隔离电源。 PC6505 芯片内部集成振荡器&am…

简单的棒棒图绘制教程

原文教程链接&#xff1a;R 语言绘图 | GO、KEGG等富集棒棒图 往期部分文章 1. 最全WGCNA教程&#xff08;替换数据即可出全部结果与图形&#xff09; WGCNA分析 | 全流程分析代码 | 代码一 WGCNA分析 | 全流程分析代码 | 代码二 WGCNA分析 | 全流程代码分享 | 代码三 WGC…

stun和trun

在 WebRTC 中&#xff0c;STUN&#xff08;Session Traversal Utilities for NAT&#xff09;和 TURN&#xff08;Traversal Using Relays around NAT&#xff09;是用于NAT穿透的两种不同的技术&#xff0c;它们解决的问题不同&#xff0c;因此在某些情况下需要同时使用。 ST…

VM虚拟机:虚拟机能ping通主机,主机ping不通虚拟机,永久解决办法。

最近在安装VM虚拟机的时候,出现了虚拟机能ping通主机,主机ping不通虚拟机。着实令人恶心,尤其是虚拟机在设置网络的时候,网上五花八门,修改什么配置的都有,最多的就是修改宿主机的ipv4,这种我个人感觉不可取。宿主机不要乱改配置,需要修改尽量在虚拟机中修改即可。 还需…

el-time-select 动态增加时间

<template><div><div v-for"(item, index) in timeSlots" :key"index"><el-time-select placeholder"起始时间" v-model"item.startTime" :picker-options"{start: 00:00,step: 00:15,end: 23:59,}"&g…

VMware安装windows虚拟机详细过程

目录 准备工作配置虚拟机为虚拟机设置操作系统镜像安装windows10 准备工作 安装好VMware软件并激活&#xff0c;激活码自行查找 准备好系统镜像文件&#xff0c;可以在MSDN中下载&#xff0c;地址&#xff1a;https://next.itellyou.cn/ 配置虚拟机 选择自定义 默认 选择稍后…

C语言操作符详解1(含进制转换,原反补码)

文章目录 一、操作符的分类二、二进制和进制转换1.二进制与十进制的相互转换2,二进制与八进制的相互转换3.二进制与十六进制的相互转换 三、原码、反码和补码四、移位操作符1.左移操作符&#xff08;1&#xff09;左移操作符移位方法&#xff08;2&#xff09;左移操作符规律总…

编程要由 “手动挡” 变 “自动挡” 了?Cursor+Claude-3.5-Sonnet,Karpathy 点赞的 AI 代码神器。如何使用详细教程

Cursor 情况简介 AI 大神 Andrej Karpathy 都被震惊了&#xff01;他最近在试用 VS Code Cursor Claude Sonnet 3.5&#xff0c;结果发现这玩意儿比 GitHub Copilot 还好用&#xff01; Cursor 在短短时间内迅速成为程序员群体的顶流神器&#xff0c;其背后的原因在于其默认使…

代码随想录 刷题记录-24 图论 (1)理论基础 、深搜与广搜

一、理论基础 参考&#xff1a; 图论理论基础 深度优先搜索理论基础 广度优先搜索理论基础 dfs dfs搜索可一个方向&#xff0c;并需要回溯&#xff0c;所以用递归的方式来实现是最方便的。 有递归的地方就有回溯&#xff0c;例如如下代码&#xff1a; void dfs(参数) {…