首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >系统崩溃分析 - vmcore 加载到 Trace32

系统崩溃分析 - vmcore 加载到 Trace32

作者头像
刘盼
发布2022-06-01 13:40:13
1.2K0
发布2022-06-01 13:40:13
举报

crash 是 Linux 内核开发中流行的调试工具。特别是它提供了强大的使用搜索命令进行内存搜索的功能。但是,它有点不方便,因为在移动每个进程的调用堆栈时没有查看局部变量的功能。

应读者要求,这篇文章,我将介绍如何从 vmcore 中提取堆栈转储并将调用堆栈上传到 Trace32。

使用命令“./crash64 vmcore vmlinux”运行崩溃实用程序。

$./crash64 vmcore vmlinux
......
please wait... (gathering kmem slab cache data)
crash64: invalid kernel virtual address: 48773a9004 type: "array cache limit"
....
WARNING: cannot determine starting stack frame for task ffffffc87bb4cc00
KERNEL: vmlinux
DUMPFILE: vmcore
CPUS: 4
DATE: Thu Jan 15 05:02:48 1987
UPTIME: 00:02:01
LOAD AVERAGE: 0.01, 0.01, 0.01
TASKS: 107
NODENAME: Xilinx-ZCU102-2016_3
RELEASE: 4.6.0
VERSION: #1 SMP Tue Apr 4 17:38:35 KST 2017
MACHINE: aarch64 (unknown Mhz)
MEMORY: 4 GB
PANIC: "sysrq: SysRq : Trigger a crash"
PID: 0
COMMAND: "swapper/0"
TASK: ffffff8008bd3400 (1 of 4) [THREAD_INFO: ffffff8008bc4000]
CPU: 0
STATE: TASK_RUNNING (ACTIVE)

查看第一条消息,命令 “echo /proc/sysrq-trigger” 导致了强制内核崩溃。

我们来看看 pid 为 1549 的 sh 进程的调用栈。

crash64> bt 1549
PID: 1549 TASK: ffffffc87bae6f00 CPU: 1 COMMAND: "sh"
#0 [ffffffc87bbdbb40] __switch_to at ffffff8008086b34
#1 [ffffffc87bbdbb60] __schedule at ffffff80088778a0
#2 [ffffffc87bbdbbb0] schedule at ffffff8008877cbc
#3 [ffffffc87bbdbbd0] schedule_timeout at ffffff800887a5a8
#4 [ffffffc87bbdbc50] wait_woken at ffffff80080d273c
#5 [ffffffc87bbdbc80] n_tty_read at ffffff80084892f8
#6 [ffffffc87bbdbd90] tty_read at ffffff8008482d10
#7 [ffffffc87bbdbdd0] __vfs_read at ffffff800818add0
#8 [ffffffc87bbdbe50] vfs_read at ffffff800818bc38
#9 [ffffffc87bbdbe90] sys_read at ffffff800818d0f8
#10 [ffffffc87bbdbed0] el0_svc_naked at ffffff8008085e2c
PC: 0000007f87126bb8 LR: 00000000004a6edc SP: 0000007fc2acbf10
X29: 0000007fc2acbf10 X28: 0000007fc2acc1e0 X27: 00000000004e5000
X26: ffffffffffffffff X25: 0000000000000001 X24: 00000000004e8a84
X23: 0000000000000001 X22: 00000000004de5f4 X21: 00000000004ea000
X20: 0000007f871ae7d0 X19: 00000000004e9a38 X18: 0000000000000001
X17: 0000007f87126bd0 X16: 0000000000000000 X15: 0000000000000070
X14: 0000000000000000 X13: 0000000000000000 X12: 0000000000000000
X11: 0000000000000000 X10: 0000000000000000 X9: 0000000000000004
X8: 000000000000003f X7: 30322d3230315543 X6: 0000007f8701c01d
X5: 248290f6fb60f00d X4: 00000000004e0000 X3: 00000000004e0000
X2: 0000000000000001 X1: 0000007fc2acbf4f X0: 0000000000000000
ORIG_X0: 0000000000000000 SYSCALLNO: 3f PSTATE: 60000000
crash64> task 1549
PID: 1549 TASK: ffffffc87bae6f00 CPU: 1 COMMAND: "sh"
struct task_struct {
state = 0x1,
stack = 0xffffffc87bbd8000, //<<--
usage = {
...
thread = {
cpu_context = {
x19 = 0xffffffc87bac4680 ,
x20 = 0xffffffc87bae6f00 , x21 =
0xffffffc87bae6f00,
x22 = 0xffffffc879160100,
x23 = 0xffffffc87bae7398,
x24 = 0xffffff8008bca000,
x25 = 0x0,
x26 = 0xffffff80092a6000,
x27 = 0x0,
x28 = 0xffffffc87b16d400,
fp = 0xffffffc87bbdbb40,
sp = 0xffffffc87bbdbb40, pc
= 0866b00

进程栈顶地址为 0xffffffc87bbd8000,在 x19~pc 字段中可以看到最后执行的寄存器集信息。

让我们转储进程堆栈。由于是 aarch64 位架构,栈大小为0x4000。所以你可以转储地址范围的堆栈。0xffffffc87bbd8000 -- 0xffffffc87bbd8000+0x4000

使用 crash 实用程序支持的 rd 命令提取内存转储。

crash64> rd 0xffffffc87bbd8000 -e 0xffffffc87bbdd000 -r stack_dump.bin

20480 字节从 0xffffffc87bbd8000 复制到 stack_dump.bin。地址0xffffffc87bbd8000是内存起始地址,0xffffffc87bbdd000是内存结束地址。将此范围内存转储保存到文件 stack_dump.bin。

通过使用 Trace32 (T32) 加载堆栈转储来恢复调用堆栈。

T32

现在轮到 T32 模拟器了。

首先,使用以下命令初始化系统。

sys.cpu cortexa53 sys.u

将以下 stack_dump.bin 转储加载到 T32。

d.load.bin stack_dump.bin /noclear 0xffffffc87bbd8000

/noclear 选项忽略并且不会覆盖以前的内存转储,其中 0xffffffc87bbd8000 是内存偏移地址。

崩溃实用程序提取了 0xffffffc87bbd8000 内存地址转储,因此 T32 上的 0xffffffc87bbd8000 给出了地址偏移量。

crash64> rd 0xffffffc87bbd8000 -e 0xffffffc87bbdd000 -r stack_dump.bin

20480 字节从 0xffffffc87bbd8000 复制到 stack_dump.bin。

让我们加载下一个 vmlinux。

Data.LOAD.elf vmlinux /nocode /noclear

现在我们已经加载了进程堆栈转储,是时候使用 T32 查看调用堆栈了。让我们首先回顾一下我们在 crash 实用程序中看到的进程寄存器信息。

thread = {
cpu_context = {
x19 = 0xffffffc87bac4680,
x20 = 0xffffffc87bae6f00 , x21 =
0xffffffc87bae6f00,
x22 = 0xffffffc879160100,
x23 = 0xffffffc87bae7398,
x24 = 0xffffff8008bca000,
x25 = 0x0,
x26 = 0xffffff80092a6000,
x27 = 0x0,
x28 = 0xffffffc87b16d400,
fp = 0xffffffc87bbdbb40,
sp = 0xffffffc87bbdbb40,
pc = 0xffffff8008086b38
},

让我们参考上面的寄存器集信息,将寄存器从 pc 加载到 T32 的 x28。在 T32 模拟器中输入以下命令。

rs pc 0xffffff8008086b38 rs sp 0xffffffc87bbdbb40 rs x29 0xffffffc87bbdbb40 rs x28 0xffffffc87b16d400 ...

在 T32 上输入“vf”命令查看调用堆栈。

-000|__switch_to(prev = 0x0, next = 0x0)
-001|__schedule(preempt = FALSE)
-002|test_bit(inline)
-002|test_ti_thread_flag(inline)
-002|need_resched(inline)
-002|schedule()
-003|schedule_timeout(timeout = 9223372036854775807)
-004|wait_woken(wait = 0xFFFFFFC87BBDBD68, ?, ?)
-005|test_ti_thread_flag(inline)
-005|test_tsk_thread_flag(inline)
-005|signal_pending(inline)
-005|n_tty_read(tty = 0xFFFFFFC87B16D400, file = 0xFFFFFFC87BA05000, buf = 0x0000007FC2ACBF4F, nr = 18446743524089577028)
-006|tty_read(file = 0xFFFFFFC87BA05000, buf = 0x0000007FC2ACBF4F, count = 1, ?)
-007|__vfs_read(file = 0x0000007FC2ACBF4F, buf = ???, count = ???, pos = ???)
-008|vfs_read(file = 0xFFFFFFC87BA05000, buf = 0x0000007FC2ACBF4F, ?, pos = 0xFFFFFFC87BBDBEC8)
-009|SYSC_read(inline)
-009|sys_read(?, buf = 548726947663, count = 1)
-010|el0_svc_naked(asm)
---|end of frame

单击 vf 窗口中的 Up 图标以移动调用堆栈。

-006|tty_read(file = 0xFFFFFFC87BA05000, buf = 0x0000007FC2ACBF4F, count = 1, ?)
-007|__vfs_read(file = 0x0000007FC2ACBF4F, buf = ???, count = ???, pos = ???)
-008|vfs_read(file = 0xFFFFFFC87BA05000, buf = 0x0000007FC2ACBF4F, ?, pos = 0xFFFFFFC87BBDBEC8)
-009|SYSC_read(inline)
-009|sys_read(?, buf = 548726947663, count = 1)
-010|el0_svc_naked(asm)
---|end of frame

本文介绍了如何通过仅从 vmcore 转储进程堆栈来查看 T32 上的调用堆栈。这样就可以在 T32 上的 vmcore 中看到内存转储。

关于 Trace32 的升级用法,我这里有一份文档,欢迎关注视频号获取。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-06-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 人人都是极客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • T32
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档