crash 是 Linux 内核开发中流行的调试工具特别是它提供了强大的使用搜索命令进行内存搜索的功能但是,它有点不方便,因为在移动每个进程的调用堆栈时没有查看局部变量的功能,我来为大家讲解一下关于系统崩溃分析怎么写?跟着小编一起来看一看吧!
系统崩溃分析怎么写
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 ffffffc87bb4cc00KERNEL: vmlinuxDUMPFILE: vmcorecpuS: 4DATE: Thu Jan 15 05:02:48 1987UPTIME: 00:02:01LOAD AVERAGE: 0.01, 0.01, 0.01TASKS: 107NODENAME: Xilinx-ZCU102-2016_3RELEASE: 4.6.0VERSION: #1 SMP Tue Apr 4 17:38:35 KST 2017MACHINE: aarch64 (unknown Mhz)MEMORY: 4 GBPANIC: "sysrq: SysRq : trigger a crash"PID: 0COMMAND: "swapper/0"TASK: ffffff8008bd3400 (1 of 4) [THREAD_INFO: ffffff8008bc4000]CPU: 0STATE: TASK_RUNNING (ACTIVE)
查看第一条消息,命令 “echo /proc/sysrq-trigger” 导致了强制内核崩溃。
我们来看看 pid 为 1549 的 sh 进程的调用栈。
crash64> bt 1549PID: 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 ffffff8008085e2cPC: 0000007f87126bb8 LR: 00000000004a6edc SP: 0000007fc2acbf10X29: 0000007fc2acbf10 X28: 0000007fc2acc1e0 X27: 00000000004e5000X26: ffffffffffffffff X25: 0000000000000001 X24: 00000000004e8a84X23: 0000000000000001 X22: 00000000004de5f4 X21: 00000000004ea000X20: 0000007f871ae7d0 X19: 00000000004e9a38 X18: 0000000000000001X17: 0000007f87126bd0 X16: 0000000000000000 X15: 0000000000000070X14: 0000000000000000 X13: 0000000000000000 X12: 0000000000000000X11: 0000000000000000 X10: 0000000000000000 X9: 0000000000000004X8: 000000000000003f X7: 30322d3230315543 X6: 0000007f8701c01dX5: 248290f6fb60f00d X4: 00000000004e0000 X3: 00000000004e0000X2: 0000000000000001 X1: 0000007fc2acbf4f X0: 0000000000000000ORIG_X0: 0000000000000000 SYSCALLNO: 3f PSTATE: 60000000
crash64> task 1549PID: 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 的升级用法,我这里有一份文档,欢迎关注视频号获取。
,