Linux 栈回溯

目录

  • 前言
  • 一、什么是栈回溯?
  • 二、栈回溯的实现原理
  • 三、参考阅读

前言

  日常工作中,我们在开发软件程序时,经常会遇到程序奔溃的问题,导致程序奔溃的原因有很多,我们一般都是定位到相关代码,再去查询具体原因。而定位到bug相关代码往往需要依赖栈回溯这个功能,知道程序是在哪里挂掉的。

一、什么是栈回溯?

  在Linux系统中,栈回溯(stack trace)是用于跟踪程序执行期间函数调用的一种技术,记录了程序在出现错误或异常时,从当前位置开始追溯回到程序的执行起点,包括每个函数的调用关系和相应的返回地址。栈回溯可以帮助开发人员快速定位问题所在。Linux系统提供了几种获取栈回溯的方法:

  1. backtrace函数:该函数定义在execinfo.h头文件中,可以获取当前线程的栈回溯信息。使用该函数需要在编译时添加-g选项以启用调试符号。使用方法:
#include <stdio.h>
#include <execinfo.h>
#include <stdlib.h>

void printStackTrace() {
  void *callstack[128];
  int i, frames;
  char **strs;

  frames = backtrace(callstack, 128);
  strs = backtrace_symbols(callstack, frames);
  
  printf("Stack Trace:\n");
  for (i = 0; i < frames; i++) {
    printf("%s\n", strs[i]);
  }
  
  free(strs);
}

void func1() {
  printf("Entering func1...\n");
  printStackTrace();
  printf("Exiting func1...\n");
}

void func2() {
  printf("Entering func2...\n");
  func1();
  printf("Exiting func2...\n");
}

void func3() {
  printf("Entering func3...\n");
  func2();
  printf("Exiting func3...\n");
}

int main() {
  printf("Entering main...\n");
  func3();
  printf("Exiting main...\n");

  return 0;
}

编译指令如下:

gcc -rdynamic stacktrace.c -o stacktrace
./stacktrace

通过添加 -rdynamic 参数,您将为可执行文件生成完整的符号表,从而使 backtrace_symbols 函数能够正确解析堆栈帧的信息。

  1. pstack命令:该命令可以打印指定进程或线程的栈回溯信息。为了使用 pstack 工具,您的系统必须安装有 gdb(GNU Debugger)。在大多数 Linux 发行版中,gdb 已经预装或可以通过包管理器轻松安装。pstack使用方法:
pstack <pid>
pstack -T <pid>
  1. gdb调试器:该调试器可以在程序崩溃时获取栈回溯信息。使用方法:
gdb <executable>
(gdb) run
<program crashes>
(gdb) bt

 无论使用哪种方法,栈回溯都可以提供关于程序崩溃或异常的有价值的信息,帮助开发人员快速定位问题所在。

二、栈回溯的实现原理

  在 Linux ARM64 系统上,实现栈回溯的原理与其他架构类似,主要涉及到寄存器、栈帧和符号表等概念。

  1. 寄存器:ARM64 架构有一组通用寄存器,用于存储函数调用的参数、局部变量和返回值等。在栈回溯过程中,关键的寄存器是程序计数器(Program Counter,PC),它保存着当前指令的地址。栈回溯需要从 PC 寄存器中获取每个函数调用的返回地址。
	ARM64 架构中的 CPU 寄存器是用于存储和处理数据的关键组件。寄存器在计算过程中用于存储
操作数、中间结果和控制信息。以下是 ARM64 架构中常见的 CPU 寄存器:

1. 通用寄存器(General-Purpose Registers):
   -3164 位的通用寄存器,用来存储整数类型数据。
   - 这些寄存器被用于算术运算、数据传输、函数参数传递和临时存储等。
   - 寄存器命名为 x0-x30,其中 x30(栈帧指针)一般作为函数的帧指针使用。

2. 程序计数器(Program Counter):
   - 存储当前执行的指令的地址。
   - 通常使用 `pc` 表示,是一个 64 位的寄存器。

3. 标志寄存器(Flags Register):
   - 存储运算结果的条件信息,例如是否溢出、是否相等等。
   - 在 ARM64 架构中,标志寄存器叫做 Condition Flags Register(CPSR)。

4. 浮点寄存器(Floating-Point Registers):
   -32128 位的浮点寄存器,用于存储浮点数和进行浮点运算。
   - 寄存器命名为 v0-v31,每个寄存器可以存储一个 128 位的浮点数或者多个较小精度的浮点数。

5. SIMD 寄存器(Single Instruction, Multiple Data Registers):
   -32128 位的 SIMD 寄存器,用于存储数据并执行 SIMD(单指令多数据)运算。
   - 在 ARM64 架构中,SIMD 寄存器被称为向量寄存器(Vector Registers)。
   - 寄存器命名为 v0-v31,每个寄存器可以存储一个 128 位的向量或者多个较小精度的元素。

	除了上述常见的寄存器之外,ARM64 架构还有一些特殊用途的寄存器,如堆栈指针寄存器
(Stack Pointer Register,SP)等。

	这些寄存器在程序的执行过程中起着重要的作用,用于处理数据、控制程序流程、传递参数等。
编程时,需要根据需求合理使用寄存器来优化性能和实现所需的功能。

在这里插入图片描述

	x0-x30 是 ARM64 架构中的通用寄存器,共有 31 个寄存器,用于存储整数类型数据以及执行
各种操作。下面对这些寄存器进行详细说明:

1. x0-x30 (x0~x30)- 这些寄存器是通用寄存器,每个寄存器的大小为 64 位。
   - 在函数调用中,寄存器 x0-x7 用于函数参数的传递,后续的参数(这几个寄存器存满了)存储在栈上。
   - 寄存器 x8 保留给系统调用使用。
   - 寄存器 x9-x15 可用作临时寄存器。
   - 寄存器 x16-x17 用作特殊用途寄存器。
   - 寄存器 x18 保留给全局数据指针使用(例如 TLS 模型)。
   - 寄存器 x19-x28 用作通用寄存器,可以用于存储数据和进行算术运算。
   - 寄存器 x29(FP,Frame Pointer)用作栈帧指针,指向当前函数的栈帧的起始位置。
   - 寄存器 x30(LR,Link Register)用于存储函数调用时的返回地址。

总体而言,x0-x30 寄存器在 ARM64 架构中用于存储数据、进行算术运算、传递函数参数、控制程序流程等。在函数调用过程中,这些寄存器的使用通过约定规则来进行管理,有助于提高运行效率和优化编译代码。开发者需要根据编程需求合理使用这些寄存器,并遵循相关规则来保证程序的正确性和性能。
  1. 栈帧:在函数调用过程中,通过栈帧(Stack Frame)来保存函数的局部变量、返回地址和其他调用相关信息。每个栈帧由保护区域(Saved Registers)和局部变量区域(Local Variables)组成。栈帧中包含了被调用函数的返回地址,在函数返回时会恢复到该地址继续执行。

  2. 栈回溯算法:栈回溯的实现通常使用递归算法或迭代算法。下面是一种迭代的栈回溯算法:

    • 在当前堆栈帧中获取当前函数的返回地址。
    • 根据地址和可执行文件的调试符号表信息,匹配到对应的函数名称和行号。
    • 打印或记录匹配到的函数名称和行号。
    • 对于从地址中解析的下一个返回地址,重复上述步骤,直到回溯到最顶层的函数或者到达设定的回溯层数。
  3. 符号表:符号表是一个映射关系,将函数名与对应的地址关联起来。在栈回溯过程中,通过符号表可以将地址解析为函数名和行号等调试信息。在 Linux 系统中,可以使用调试符号表文件(例如 ELF 文件)来获取符号表信息。

  需要注意的是,栈回溯的准确性和可读性受到符号表的影响。如果可执行文件没有包含调试符号信息,或者没有正确设置符号文件的路径,栈回溯可能只能提供地址而无法解析为函数名和行号。因此,在构建可执行文件时,建议开启调试符号信息的生成并妥善保存符号表文件。

三、参考阅读

ARM体系结构:https://zhuanlan.zhihu.com/p/577979125?utm_id=0
内核中dump_stack:https://www.cnblogs.com/pengdonglin137/p/11109427.html
dump_stack:https://blog.csdn.net/weixin_52849254/article/details/130559085

  dump_stack 函数是 Linux 内核中的一个调试函数,用于在内核代码中打印当前的函数调用堆栈信息。它用于诊断和调试内核中发生的问题,如内核崩溃或死锁等。

  dump_stack 函数的原型定义在 kernel/lib/dump_stack.c 头文件中:

void dump_stack(void);
	__dump_stack();
		dump_stack_print_info(KERN_DEFAULT);
		show_stack(NULL, NULL) //arch\arm64\kernel\traps.c

  通过调用 dump_stack 函数,可以在内核日志中输出当前的函数调用堆栈信息。这对于定位问题非常有用,特别是在内核崩溃时,您可以通过查看内核日志中的堆栈跟踪信息来确定哪些函数导致了问题。

  要在内核代码中使用 dump_stack 函数,只需在适当的位置调用它即可。例如,在驱动程序中遇到错误或异常情况时,可以在相应的错误处理路径中调用 dump_stack 函数,以便在内核日志中获取有关问题的更多信息。

注:推荐一本讲调试技巧的书《Debug Hacks中文版—深入调试的技术和工具》,非常nice!!!

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

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

相关文章

FinOps和DevOps的未来会怎样?

FinOps&#xff08;或财务运营&#xff09;是一种文化实践&#xff0c;它将财务责任引入云的可变支出模型。这是一种将系统、最佳实践和文化相结合的战略方法&#xff0c;可提高组织了解云成本并做出明智决策的能力。 本质上&#xff0c;FinOps 是一个管理云运营费用&#xff…

C++:OJ练习(每日练习系列)

编程题&#xff1a; 题一&#xff1a;把字符串转换成整数 把字符串转换成整数_牛客题霸_牛客网 示例1 输入&#xff1a; "2147483647" 返回值&#xff1a; 2147483647思路一&#xff1a; 第一步&#xff1a;it从str的第一个字符开始遍历&#xff0c;定义一个最后输…

使用 ChatGPT 创建 Makefile 构建系统:从 Docker 开始

使用 Docker 搭配 ChatGPT 创建 Makefile 构建系统 Makefile 构建系统是嵌入式软件团队实现其开发流程现代化的基础。构建系统不仅允许开发人员选择各种构建目标&#xff0c;还可以将这些构建集成到持续集成/持续部署 (CI/CD) 流程中。使用诸如 ChatGPT 这样的人工智能 (AI) 工…

【docker】docker的基础命令

基础操作 docker info #查看docker的基本信息docker version #查看docker版本信息一、镜像操作 1、搜索镜像 docker search nginx2、下载镜像 docker pull nginx#从仓库中下载镜像&#xff0c;若没有指定标签&#xff0c;则下载最新的版本&#xff0c;也就是标签为: lat…

使用opencv实现图像滤波

1 图像滤波介绍 滤波是信号和图像处理中的基本任务之一&#xff0c;其旨在有选择地提取图像的某些特征&#xff0c;可以用于在给定应用程序的上下文中传达重要信息&#xff0c;例如&#xff0c;去除图像中的噪声、提取所需的视觉特征、图像重采样等。 1.1 图像滤波理论 图像…

Matplotlib线形图的创建_Python数据分析与可视化

线形图的创建 绘制线形图设置颜色和风格设置坐标轴上下限设置图形标签 绘制线形图 在所有图形中&#xff0c;最简单的应该就是线性方程y f (x) 的可视化了。来看看如何创建这个简单的线形图。要画Matplotlib图形时&#xff0c;都需要先创建一个图形fig 和一个坐标轴ax。创建图…

AI - Crowd Simulation(集群模拟)

类似鱼群&#xff0c;鸟群这种群体运动模拟。 是Microscopic Models 微观模型&#xff0c;定义每一个个体的行为&#xff0c;然后合在一起。 主要是根据一定范围内族群其他对象的运动状态决定自己的运动状态 Cohesion 保证个体不会脱离群体 求物体一定半径范围内的其他临近物…

深度学习回顾:七种网络

一、说明 本文 揭开CNN、Seq2Seq、Faster R-CNN 和 PPO &#xff0c;以及transformer和humg-face— 编码和创新之路。对于此类编程的短小示例&#xff0c;用于对照观察&#xff0c;或做学习实验。 二、CNN网络示例 2.1 CNN用mnist数据集 CNN 专为图像处理而设计&#xff0c;包…

idea创建不了spring2.X版本,无法使用JDK8,最低支持JDK17 , 如何用idea创建spring2.X版本,使用JDK8解决方案

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信您对博主首页也很感兴趣o (ˉ▽ˉ&#xff1b;) &#x1f4dc;jdk17安装全方位手把手安装教程 / 已有jdk8了&#xff0c;安装JDK17后如何配置环境变量 / 多个不同版本的JDK&#xff0c;如何配置环境变量&a…

FreeRTOS源码阅读笔记6--event_groups.c

通常用的事件标志组是一个32位的变量uxEventBits&#xff0c;可设置的位有24位&#xff0c;一共就是24 种事件。 事件组的结构体类型&#xff1a; 6.1创建事件组xEventGroupCreate() 6.1.1函数原型 返回值&#xff1a;事件组句柄&#xff0c;指向事件组。 6.1.2函数框架 ①…

通过亚马逊云科技云存储服务探索云原生应用的威力

文章作者&#xff1a;Libai 欢迎来到我们关于“使用亚马逊云科技云存储服务构建云原生应用”的文章的第一部分。在本文中&#xff0c;我们将深入探讨云原生应用的世界&#xff0c;并探索亚马逊云科技云存储服务在构建和扩展这些应用中的关键作用。 亚马逊云科技开发者社区为开发…

鸿蒙应用开发-初见:ArkUI

编程范式&#xff1a;命令式->声明式 以一个卡片的实现做下讲解 命令式 简单讲就是需要开发用代码一步一步进行布局&#xff0c;这个过程需要开发全程参与。 Objective-C UIView *cardView [[UIView alloc] init]; cardView.backgroundColor [UIColor whiteColor]; ca…

[socket 弹 shell] msg_box3

前言 题目比较简单&#xff0c;没开 Canary 和 NX. Arch: amd64-64-littleRELRO: Full RELROStack: Canary foundNX: NX disabledPIE: PIE enabledRWX: Has RWX segments 漏洞利用与分析&#xff1a; 白给的函数调用&#xff0c;其中 ptr 10 是用…

Elasticsearch启动失败问题汇总

版本elasticsearch-8.11.1&#xff0c;解压安装完后&#xff0c;修改安装目录下conf/jvm.options&#xff0c; 默认配置如下&#xff1a; -Xms4g -Xmx4g 默认的配置占用内存太多了&#xff0c;调小一些&#xff1a; -Xms256m -Xmx256m由于es和jdk是一个强依赖的关系&#xff0…

[黑马程序员SpringBoot2]——开发实用篇3

目录&#xff1a; jetcache远程缓存方案jetcache本地缓存方案jetcache方法缓存j2cache基本操作springboot整合quartz​​​​​​​springboot整合task发送简单邮件发送多部件邮件消息简介购物订单案例-发送短信ActiveMQ安装springboot整合ActiveMQRabbitMQ安装springboot整合…

代理IP可以用于哪些实际场景?遇到问题如何解决

随着互联网的普及和网络应用的广泛使用&#xff0c;代理IP已成为许多人工作和生活中不可或缺的一部分。代理IP可以用于多种实际场景&#xff0c;并在遇到问题时提供有效的解决方案。下面将详细介绍代理IP的实际应用场景及遇到问题时的解决方法。 一、代理IP的实际应用场景 1. 网…

C#,数值计算——插值和外推,径向基函数插值(RBF_interp)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// 径向基函数插值 /// Object for radial basis function interpolation using n points in dim /// dimensions.Call constructor once, then interp as many times as desir…

阿里云服务器部署node和npm

目录 1.链接服务器2.找到node 下载地址3获取链接地址4下载到linux5.解压6.重命名 解压后的文件7.配置环境变量7.1复制当前的bin目录7.2vim /etc/profile7.3在按下ESC按键 8.重启环境变量9.输入node10.npm配置加速镜像 1.链接服务器 2.找到node 下载地址 https://nodejs.org/d…

激光雷达毫米波雷达

一.激光雷达 技术指标&#xff1a; 视场角 线数&#xff08;32/64/128&#xff09; 分辨率&#xff08;激光光束夹角越小分辨率越高&#xff0c;0.1度&#xff09; 探测距离&#xff1a;0.3-200m 反射率&#xff08;一般探测10%以上反射率的目标&#xff09; 分类 按照测距方…

JUC下常用的类

一、Semaphore 信号量 new Semaphore(10) 可以把他理解成停车场&#xff0c;最多停10辆车&#xff0c;多个车进来如果满了就去排队&#xff0c;车走了&#xff0c;车位就空出来了&#xff0c;排队的线程就可以进来主要下面2个方法 Acquire获取锁&#xff1a;通过CAS原子性减1&…