用trace32分析内核死机(内核call trace)
nanshan 2024-10-10 07:23 34 浏览 0 评论
dmesg 初步分析
[ 423.400073] Unable to handle kernel NULL pointer dereference at virtual address 00000008
[ 423.400075] [silead finger_interrupt_handler 505]:S IRQ 19 , GPIO 12 state is 0
[ 423.400083] [silead finger_interrupt_handler 506]:state is 0
[ 423.400096] pgd = ffffffc0017eb000
[ 423.400103] [00000008] *pgd=000000008ea0a003, *pud=000000008ea0a003, *pmd=000000008ea0b003, *pte=006000000b000707
[ 423.400124] Internal error: Oops: 96000046 [#1] PREEMPT SMP
[ 423.400132] Modules linked in: wlan(O)
[ 423.400148] CPU: 4 PID: 0 Comm: swapper/4 Tainted: G W O 3.18.31-perf #1
[ 423.400155] Hardware name: Qualcomm Technologies, Inc. MSM8940-PMI8950 MTP (DT)
[ 423.400164] task: ffffffc04eae4980 ti: ffffffc0b28bc000 task.ti: ffffffc0b28bc000
[ 423.400182] PC is at run_timer_softirq+0x4ac/0x5ec
[ 423.400192] LR is at run_timer_softirq+0x324/0x5ec
[ 423.400199] pc : [<ffffffc0000feb98>] lr : [<ffffffc0000fea10>] pstate: 600001c5
[ 423.400204] sp : ffffffc0b28bfb60
...
[ 423.401490] Process swapper/4 (pid: 0, stack limit = 0xffffffc0b28bc058)
[ 423.401496] Call trace:
[ 423.401510] [<ffffffc0000feb98>] run_timer_softirq+0x4ac/0x5ec
[ 423.401523] [<ffffffc0000a6864>] __do_softirq+0x178/0x350
[ 423.401532] [<ffffffc0000a6c8c>] irq_exit+0x74/0xb0
[ 423.401543] [<ffffffc0000edf18>] __handle_domain_irq+0xb4/0xec
[ 423.401553] [<ffffffc00008254c>] gic_handle_irq+0x54/0x84
[ 423.401560] Exception stack(0xffffffc0b28bfd40 to 0xffffffc0b28bfe60)
...
[ 423.401671] [<ffffffc000085da8>] el1_irq+0x68/0xd4
[ 423.401685] [<ffffffc000851480>] cpuidle_enter_state+0xd0/0x224
[ 423.401695] [<ffffffc0008516ac>] cpuidle_enter+0x18/0x20
[ 423.401706] [<ffffffc0000e1cc0>] cpu_startup_entry+0x288/0x384
[ 423.401717] [<ffffffc000091d5c>] secondary_start_kernel+0x108/0x114
[ 423.401728] Code: b90052a0 34000840 f9400321 f9400720 (f9000420)
[ 423.401736] ---[ end trace d0daa1892c14378b ]---
[ 423.401753] Kernel panic - not syncing: Fatal exception in interrupt
[ 423.401774] CPU7: stopping
连上trace32,load vmlinux后通过list source看下汇编源码混合显示,如下:
- FFFFFFC0000FE968: 当前汇编指令的虚拟地址
- F9000760: 汇编机器码,ARM/ARM64的指令机器码都是32位固定长度
- str x0,[x27, #0x8]: 汇编指令,;后的是注释
根据AAPCS(ARM二进制过程调用标准)参数传递规则,ARM64的 v0 - v7 参数直接由 x0 - x7 传递,其他参数由压栈传递,子程序返回结果保存到x0。
那么这里可推导如下:
x0 == prev, x1 == next
指令:str x0 ,[x1,#0x8]
x1+0x8 其实就是next+8个字节的偏移,看下:
struct list_head {
struct list_head *next, *prev;
};
ARM体系结构中,ARM64一个指针占8个字节内存,也就是: [x1+0x8] == prev
所以这个str指令就是对应上面的next->prev = prev
我们根据异常栈的寄存器值来看下:
[ 423.400182] PC is at run_timer_softirq+0x4ac/0x5ec
[ 423.400192] LR is at run_timer_softirq+0x324/0x5ec
[ 423.400199] pc : [<ffffffc0000feb98>] lr : [<ffffffc0000fea10>] pstate: 600001c5
[ 423.400204] sp : ffffffc0b28bfb60
[ 423.400210] x29: ffffffc0b28bfb60 x28: ffffffc0b2619038
[ 423.400219] x27: ffffffc000c9a000 x26: 0000000000000000
[ 423.400228] x25: ffffffc001741120 x24: ffffffc0006e277c
[ 423.400237] x23: ffffffc0b2619000 x22: ffffffc0b28bfbf8
[ 423.400246] x21: ffffffc0b28bc000 x20: ffffffc0013d2000
[ 423.400254] x19: ffffffc0b2618000 x18: 0000007f9588e080
[ 423.400263] x17: 0000007f9a36d4b4 x16: ffffffc0001e4f6c
[ 423.400272] x15: 003b9aca00000000 x14: 0000000000000001
[ 423.400280] x13: 0000000000000000 x12: 0000000000000001
[ 423.400289] x11: 000000000000000f x10: ffffffc000c9c3f4
[ 423.400297] x9 : 0000000000000000 x8 : 0000000000000005
[ 423.400305] x7 : 0000000000000000 x6 : 000000000001451c
[ 423.400314] x5 : ffffffc0b2ae8000 x4 : 00135f1aa15bb200
[ 423.400323] x3 : 0000000000000018 x2 : 0000000000000000
[ 423.400331] x1 : 0000000000000000 x0 : ffffffc0b28bfbf8
上面可以看到,x1+0x8 ==0x0000000000000000+0x8==0x0000000000000008,这个和出错时候报的地址一致“Unable to handle kernel NULL pointer dereference at virtual address 00000008”
因为ARM64内核的虚拟地址空间范围是0xFFFF_0000_0000_0000 =>0xFFFF_FFFF_FFFF_FFFF,很明显这个值0x0000000000000008不在范围内,它属于用户空间的虚拟地址空间,肯定会被MMU拦截掉上报data abort异常,所以此题的真正原因是程序跑飞访问非法地址所致。
目前看来从kernel log上的信息无法直接分析出导致问题的具体源代码,从dmesg的这些信息我们已经知道出问题的是这个prev指针,但是比较难直接抓到导致异常的真凶源码位置。
Trace32 分析
利用dmesg我们分析了产生问题时候的来龙去脉,但是想要彻底解决还需要trace32进一步分析。
输入v.f,调出当前的调用栈关系:
可以看到,异常时候的各种参数都显示出来了,这样就非常有利于我们debug了,这也是单纯从dmesg无法得到的重要信息!注意inline类型的函数会被编译器默认优化掉,所以inline类型的函数的参数不可见,需要通过读汇编代码,分析寄存器传参推导。
输入d.list 查看PC停止的位置,如下高亮:
分析Call Stack:
为方便查看,把调用栈信息复制出来,如下:
1. ...
-007|die(
| ?,
| regs = 0xFFFFFFC0B28BFA40 -> (
| user_regs = (regs = (0xFFFFFFC0B28BFBF8, 0x0, 0x0, 0x18, 0x00135F1AA15BB200, 0xFFFFFFC0B2AE800
| regs = (0xFFFFFFC0B28BFBF8, 0x0, 0x0, 0x18, 0x00135F1AA15BB200, 0xFFFFFFC0B2AE8000, 0x0001451C
| sp = 0xFFFFFFC0B28BFB60,
| pc = 0xFFFFFFC0000FEB98,
| pstate = 0x600001C5,
| orig_x0 = 0xFFFFFFC0B2618000,
| syscallno = 0xFFFFFFC0000FE7D0),
| err = 0x96000046)
| flags = 0x01C0
| ret = 0x1
| tsk = 0xFFFFFFC04EAE4980
| die_counter = 0x1
-008|__do_kernel_fault.part.5(
| mm = 0x0,
| addr = 0x8,
| esr = 0x96000046,
| regs = 0xFFFFFFC0B28BFA40)
-009|do_page_fault(
| addr = 0x8,
| esr = 0x96000046,
| regs = 0xFFFFFFC0B28BFA40)
| tsk = 0xFFFFFFC04EAE4980
| mm = 0x0
| vm_flags = 0xFFFFFFC000C9A000
| vma = 0xFFFFFFC0B28BFA40
-010|do_translation_fault(
| addr = 0x0A44,
| esr = 0x0124F800,
| ?)
-011|do_mem_abort(
| addr = 0x8,
| esr = 0x96000046,
| regs = 0xFFFFFFC0B28BFA40)
| inf = 0xFFFFFFC0013DC790 -> (
| fn = 0xFFFFFFC000099A74,
| sig = 0x0B,
| code = 0x00030001,
| name = 0xFFFFFFC0010DF250 -> 0x6C)
| info = (
| si_signo = 0x0032D110,
| si_errno = 0xFFFFFFC0,
| si_code = 0x01C0,
| _sifields = (_pad = (0x7, 0x0, 0xB28BF9E0, 0xFFFFFFC0, 0x000A6D78, 0xFFFFFFC0, 0xB28BF9F0, 0xFFF
-012|el1_da(asm)
-->|exception
-013|__list_del(inline)
-013|detach_timer(inline)
-013|detach_expired_timer(inline)
-013|__run_timers(inline)
-013|run_timer_softirq(
| ?)
| base = 0xFFFFFFC0B2618000 -> (
| lock = (rlock = (raw_lock = (owner = 0x6FCD, next = 0x6FCE))),
| running_timer = 0xFFFFFFC001741120 -> (
| entry = (next = 0xFFFFFFC0B27BC9B8, prev = 0xFFFFFFC0B27BC9B8),
| expires = 0x0000000100003098,
| base = 0xFFFFFFC0B27BC000,
| function = 0xFFFFFFC0006E277C -> ,
| data = 0x0,
| slack = 0xFFFFFFFF,
| start_pid = 0xFFFFFFFF,
| start_site = 0x0,
| start_comm = (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0))
| timer_jiffies = 0x0000000100003035,
| next_timer = 0x0000000100003034,
| active_timers = 0x7,
| all_timers = 0x7,
| cpu_=_0x4,
| tv1 = (vec = ((next = 0xFFFFFFC0B2618038, prev = 0xFFFFFFC0B2618038), (next = 0xFFFFFFC0B2618048
| tv2 = (vec = ((next = 0xFFFFFFC0B2619038, prev = 0xFFFFFFC0B2619038), (next = 0xFFFFFFC0B2619048
| tv3 = (vec = ((next = 0xFFFFFFC0B2619438, prev = 0xFFFFFFC0B2619438), (next = 0xFFFFFFC0B2619448
| tv4 = (vec = ((next = 0xFFFFFFC0B2619838, prev = 0xFFFFFFC0B2619838), (next = 0xFFFFFFC0B2619848
| tv5 = (vec = ((next = 0xFFFFFFC0B2619C38, prev = 0xFFFFFFC0B2619C38), (next = 0xFFFFFFC0B2619C48
| fn = 0xFFFFFFC0006E277C ->
| data = 0x0
| it_func_ptr = 0x0
...
看到这里,我们可以猜想是不是run_timer_softirq的参数出现了问题导致后面发生的一系列异常?可以从这个方向开始思考,我们先来看下这个函数的实现:
static void run_timer_softirq(struct softirq_action *h)
{
struct tvec_base *base = __this_cpu_read(tvec_bases);
hrtimer_run_pending();
__run_deferrable_timers();
if (time_after_eq(jiffies, base->timer_jiffies))
__run_timers(base);
}
我们看到这个函数最重要的参数变量就是这个base,传入的h没有使用,继续来看下base的结构tvec_base :
struct tvec_base {
spinlock_t lock;
struct timer_list *running_timer;
unsigned long timer_jiffies;
unsigned long next_timer;
unsigned long active_timers;
unsigned long all_timers;
int cpu; // 跟踪所在的CPU是哪个核,这里是CPU 4
struct tvec_root tv1;
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
} ____cacheline_aligned;
这里就看到 tvec_base 的结构里面有个 struct timer_list * 的结构,我们继续看它的结构是怎么样的:
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
unsigned long expires;
struct tvec_base *base;
void (*function)(unsigned long);
unsigned long data;
int slack;
...
首先我们查看 running_timer 的数据内容,
工具栏调出:view -> Watch,输入:(struct timer_list *)0xffffffc001741120
这个就是发生异常的那个timer的数据结构实例,我们最希望的就是希望可以通过这里的数据信息找到它在源码的位置,然后进一步分析它,使用Trace32的 dump 分析功能就可以做到这点。
菜单栏调出:view -> dump
输入地址 0xffffffc001741120 然后点OK
右击高亮,选择view info:
同理,还可以看function的位置(0xFFFFFFC0006E277C):
上面所示,出异常的timer实例就是:fp_drv/m_timer, callback = timer_out_handle,源码位置也给出来了,那么就可以着手修复问题了。
相关推荐
- 0722-6.2.0-如何在RedHat7.2使用rpm安装CDH(无CM)
-
文档编写目的在前面的文档中,介绍了在有CM和无CM两种情况下使用rpm方式安装CDH5.10.0,本文档将介绍如何在无CM的情况下使用rpm方式安装CDH6.2.0,与之前安装C5进行对比。环境介绍:...
- ARM64 平台基于 openEuler + iSula 环境部署 Kubernetes
-
为什么要在arm64平台上部署Kubernetes,而且还是鲲鹏920的架构。说来话长。。。此处省略5000字。介绍下系统信息;o架构:鲲鹏920(Kunpeng920)oOS:ope...
- 生产环境starrocks 3.1存算一体集群部署
-
集群规划FE:节点主要负责元数据管理、客户端连接管理、查询计划和查询调度。>3节点。BE:节点负责数据存储和SQL执行。>3节点。CN:无存储功能能的BE。环境准备CPU检查JDK...
- 在CentOS上添加swap虚拟内存并设置优先级
-
现如今很多云服务器都会自己配置好虚拟内存,当然也有很多没有配置虚拟内存的,虚拟内存可以让我们的低配服务器使用更多的内存,可以减少很多硬件成本,比如我们运行很多服务的时候,内存常常会满,当配置了虚拟内存...
- 国产深度(deepin)操作系统优化指南
-
1.升级内核随着deepin版本的更新,会自动升级系统内核,但是我们依旧可以通过命令行手动升级内核,以获取更好的性能和更多的硬件支持。具体操作:-添加PPAs使用以下命令添加PPAs:```...
- postgresql-15.4 多节点主从(读写分离)
-
1、下载软件[root@TX-CN-PostgreSQL01-252software]#wgethttps://ftp.postgresql.org/pub/source/v15.4/postg...
- Docker 容器 Java 服务内存与 GC 优化实施方案
-
一、设置Docker容器内存限制(生产环境建议)1.查看宿主机可用内存bashfree-h#示例输出(假设宿主机剩余16GB可用内存)#Mem:64G...
- 虚拟内存设置、解决linux内存不够问题
-
虚拟内存设置(解决linux内存不够情况)背景介绍 Memory指机器物理内存,读写速度低于CPU一个量级,但是高于磁盘不止一个量级。所以,程序和数据如果在内存的话,会有非常快的读写速度。但是,内存...
- Elasticsearch性能调优(5):服务器配置选择
-
在选择elasticsearch服务器时,要尽可能地选择与当前业务量相匹配的服务器。如果服务器配置太低,则意味着需要更多的节点来满足需求,一个集群的节点太多时会增加集群管理的成本。如果服务器配置太高,...
- Es如何落地
-
一、配置准备节点类型CPU内存硬盘网络机器数操作系统data节点16C64G2000G本地SSD所有es同一可用区3(ecs)Centos7master节点2C8G200G云SSD所有es同一可用区...
- 针对Linux内存管理知识学习总结
-
现在的服务器大部分都是运行在Linux上面的,所以,作为一个程序员有必要简单地了解一下系统是如何运行的。对于内存部分需要知道:地址映射内存管理的方式缺页异常先来看一些基本的知识,在进程看来,内存分为内...
- MySQL进阶之性能优化
-
概述MySQL的性能优化,包括了服务器硬件优化、操作系统的优化、MySQL数据库配置优化、数据库表设计的优化、SQL语句优化等5个方面的优化。在进行优化之前,需要先掌握性能分析的思路和方法,找出问题,...
- Linux Cgroups(Control Groups)原理
-
LinuxCgroups(ControlGroups)是内核提供的资源分配、限制和监控机制,通过层级化进程分组实现资源的精细化控制。以下从核心原理、操作示例和版本演进三方面详细分析:一、核心原理与...
- linux 常用性能优化参数及理解
-
1.优化内核相关参数配置文件/etc/sysctl.conf配置方法直接将参数添加进文件每条一行.sysctl-a可以查看默认配置sysctl-p执行并检测是否有错误例如设置错了参数:[roo...
- 如何在 Linux 中使用 Sysctl 命令?
-
sysctl是一个用于配置和查询Linux内核参数的命令行工具。它通过与/proc/sys虚拟文件系统交互,允许用户在运行时动态修改内核参数。这些参数控制着系统的各种行为,包括网络设置、文件...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- linux 查询端口号 (58)
- docker映射容器目录到宿主机 (66)
- 杀端口 (60)
- yum更换阿里源 (62)
- internet explorer 增强的安全配置已启用 (65)
- linux自动挂载 (56)
- 禁用selinux (55)
- sysv-rc-conf (69)
- ubuntu防火墙状态查看 (64)
- windows server 2022激活密钥 (56)
- 无法与服务器建立安全连接是什么意思 (74)
- 443/80端口被占用怎么解决 (56)
- ping无法访问目标主机怎么解决 (58)
- fdatasync (59)
- 405 not allowed (56)
- 免备案虚拟主机zxhost (55)
- linux根据pid查看进程 (60)
- dhcp工具 (62)
- mysql 1045 (57)
- 宝塔远程工具 (56)
- ssh服务器拒绝了密码 请再试一次 (56)
- ubuntu卸载docker (56)
- linux查看nginx状态 (63)
- tomcat 乱码 (76)
- 2008r2激活序列号 (65)