内核崩溃kdump(sysdump)和crash分析
系统一旦崩溃,内核就没法正常工作了,这个时候需要触发一种转存储机制(kernel中的kdump, unisoc的sysdump)。转存机制提供一个用于捕获当前运行现场的内核,该内核会将此时内存中的所有运行状态和数据信息收集到一个dump core文件中以便之后分析崩溃原因。
在系统发生诸如Kernel crash等异常时,在Kernel中完成flush cache等处理后,重启进入dump内核收集和保存现场数据。
crash, 是某个特定时刻(如kernel panic)的系统RAM的快照,需要转存的现场数据。
约束条件:
kdump 、sysdump 通常用于假死机(unresposive)和panic,也就是没有响应的情况下。硬件问题导致的死机,无能为力。
1.kdump
kdump是kernel原生的一个系统奔溃时的现场转存工具。kdump是RHEL5之后才支持的,2006被主线接收为内核的一部分。它的原理简单来说是在内存中保留一块区域,这块区域用来存放capture kernel,当production kernel发生crash的时候,通过kexec把保留区域的capure kernel给运行起来,再由捕获内核负责把产品内核的完整信息,包括CPU寄存器、堆栈数据等转储到指定位置的文件中。
(1) kdump运行原理介绍
kdump实现了"双内核"布局,Kdump 在内核在内核panic后,立即调用kexec 引导到转储捕获内核(capture kernel),使用 kexec 引导 “覆盖” 当前运行的内核。
该“转储捕获内核”的内存区域由主内核的bootargs参数 crashkernel 或dts指定。“转储捕获内核”可以是专门build的单独 Linux 内核image,也可以在支持可重定位内核的系统架构上重用主内核映像。
kexec(kernel execution,类似于 Unix 或 Linux 的系统调用 exec)是 Linux 内核的一种机制,其允许从当前运行的内核启动新内核。kexec 会跳过由系统固件(BIOS或UEFI、bootloader)执行的引导加载程序阶段和硬件初始化阶段,直接将新内核加载到主内存并立即开始执行。这避免了完全重新启动的漫长时间,并且可以通过最小化停机时间来满足系统高可用性要求。
注意: 不经过bootloader或bios阶段,直接从主kernel启动到“转储捕获内核”进行。
(2) dump触发方式
1.手动触发
- 通过sysrq触发
echo c /proc/sysrq-trigger
- 通过IPMI触发不可屏蔽中断
ipomitool power diag
- 通过virsh触发不可屏蔽中断
virsh inject-nmi MyGuestName
- Beware of
kernel.unkonow_nmi_panic=1
2.自动触发
- watchdong(看门狗)
cmdline中:nmi_watchdog=1
- softlockup(软锁)
sysctl kernel.softlockup_panic=1
- 内存越界
sysctl vm.panic_on_oom=1
2.sysdump
SysDump即Dump system memory,是sysdump的一种转存储机制,是将发生异常时的内存信息、寄存器信息等有效信息转存为文件,以便于借助分析工具分析问题现场。
在系统发生诸如Kernel crash等异常时,在Kernel中完成flush cache等处理后,重启进入Uboot(或LK等bootloader阶段)中完成所有数据的保存,保存过程会有相应屏幕提示,完成后根据屏幕提示重启手机,导出异常数据文件进行分析。
SysDump分为FullDump和MiniDump两个子功能,两个子功能是互不影响的。
- FullDump保存完整DDR信息,可以支持两种存储路径Dump2SD和Dump2PC。Dump2SD功能依赖于SD卡,即设备需支持插入SD卡。
- MiniDump保存少量信息到单独的SysDumpdb分区,然后再由native sevice 程序将分区保存的raw数据整理解析后放置到/sdcard/MiniDump路径下。
MiniDump功能仅Android系统的设备支持。
在资源允许的条件下,优先选择保存分析FullDump日志,因为其保存了完整的现场信息快照,更有利于深入分析问题。
(1) sysdump运行原理介绍
系统异常和组合键主动触发系统异常都会走到Kernel的处理流程,但长按7s(秒)的操作不会进入Kernel处理流程。
- MiniDump在Kernel阶段完成初始化和异常处理时的数据保存。
- Uboot中完成数据的存储操作,包括FullDump 和MiniDump。
(2) dump触发方式
硬件配置不同,SysDump触发方式也有所差异,具体如下。
3.转存文件的解析—crash工具
前面提到,当系统崩溃时,通过kdump或sysdump可以获得当时的内存转储文件vmcore,但是该如何分析vmcore呢?
crash是一个用于分析内核转储文件的工具,一般和kdump搭配使用,sysdump也可以使用该工具解析。
使用crash时,要求调试内核vmlinux在编译时带有-g选项,即带有调试信息。如果没有指定vmcore,则默认使用实时系统的内存来分析。
值得一提的是,crash也可以用来分析实时的系统内存,是一个很强大的调试工具。crash使用gdb作为内部引擎,语法类似于gdb,命令的使用说明可以用 help来查看。
(1) crash工具的依赖
使用crash需要安装crash工具包和内核调试信息包:
crash
kernel-debuginfo-common
kernel-debuginfo
对于在PC上调试arm平台的异常现场,使用专用的crash工具,不需要安装,直接运行。
工具获取路径:
工具说明:
crash_arm:用于32bit ARM 平台
crash_arm64:用于64bit ARM 平台
(2) crash工具的使用
使用crash来调试vmcore,至少需要两个参数:
- 未压缩的内核映像文件vmlinux。ubuntu默认位于/usr/lib/debug/lib/modules/$(uname -r)/vmlinux,由内核调试信息包提供。
- 内存转储文件vmcore,由kdump或sysdump转存的内核奔溃现场快照。
对于ARM平台来说,除上述参数之外,一般还需要指定如下参数:
- phys_offset: RAM的起始物理地址
- vabits_actual: 实际的虚拟地址空间大小(总线位宽)
- kimage_voffset: kernel image映射的物理地址偏移
以unisoc ARM64平台为例,crash执行命令:
./crash_arm64 -m vabits_actual=39 -m phys_offset=0x80000000 -m kimage_voffset=0xffffffbf90000000 ./vmlinux ./vmcore
(3) crash基本输出分析
crash基本输出:
~/crash$ ./crash_arm64_v8 -m vabits_actual=39 -m phys_offset=0x80000000 -m kimage_voffset=0xffffffbf90000000 ./vmlinux ./syscore
crash_arm64_v8 8.0.0++
Copyright (C) 2002-2021 Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation
Copyright (C) 1999-2006 Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited
Copyright (C) 2006, 2007 VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011, 2020-2021 NEC Corporation
Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
Copyright (C) 2015, 2021 VMware, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions. Enter "help copying" to see the conditions.
This program has absolutely no warranty. Enter "help warranty" for details.
NOTE: setting vabits_actual to: 39
NOTE: setting phys_offset to: 0x80000000
NOTE: setting kimage_voffset to: 0xffffffbf90000000
GNU gdb (GDB) 10.2
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=aarch64-elf-linux".
Type "show configuration" for configuration details.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
WARNING: cpu 0: cannot find NT_PRSTATUS note
KERNEL: ./vmlinux [TAINTED]
DUMPFILE: ./syscore
CPUS: 8 [OFFLINE: 7]
DATE: Wed Sep 14 16:38:14 CST 2022
UPTIME: 01:48:53
LOAD AVERAGE: 10.46, 10.99, 10.55
TASKS: 1564
NODENAME: localhost
RELEASE: 5.4.190-android12-9-02871-g77dc16611297
VERSION: #1 SMP PREEMPT Wed Sep 14 10:55:40 CST 2022
MACHINE: aarch64 (unknown Mhz)
MEMORY: 3 GB
PANIC: "Kernel panic - not syncing: sysrq triggered crash"
PID: -1
COMMAND: "bash"
TASK: ffffff801e604b00 (1 of 32) [THREAD_INFO: ffffff801e604b00]
CPU: -1
STATE: TASK_INTERRUPTIBLE|TASK_UNINTERRUPTIBLE|TASK_STOPPED|TASK_TRACED|EXIT_DEAD|TASK_DEAD|EXIT_ZOMBIE|TASK_WAKING|TASK_WAKEKILL|TASK_NOLOAD|TASK_NEW
crash_arm64_v8>
这些基本输出信息简单明了,可由sys命令触发。
其中各项参数的意义为:
- KERNEL表示调试用内核的位置和版本信息
- DUMPFILE: 表示所分析的内存转储镜像
- CPUS: 表示本机的 CPU 数目
- DATE: 表示内核崩溃发生的时间
- UPTIME: 表示内核已正常运行的时间
- LOAD AVERAGE: 表示内核崩溃时系统的负载
- TASKS: 表示内核崩溃时系统运行的任务数
- NODENAME: 表示内核崩溃的机器的主机名
- RELEASE: 表示内核的发布版本
- VERSION: 表示内核的其他版本信息
- MACHINE: 表示 CPU 的架构和主频信息
- MEMORY: 表示发生内核崩溃的系统的内存大小
- PANIC: 表示内核崩溃的类型
- PID: 表示导致内核崩溃的进程号;
- COMMAND: 表示导致内核崩溃的进程名称
- TASK: 表示导致内核崩溃的进程访问的内存地址;
- CPU: 表示导致内核崩溃的进程占用的 CPU 数目;
- STATE: 表示导致内核崩溃的进程的运行状态。
需要重点关注的是 13. PANIC,它告诉我们内核奔溃的原因。一般有如下类型:
- SysRq。即通过系统请求造成的内核崩溃,如上面测试用的命令。
- Oops。表示内核发生了不可预期的或不正确的行为,这时会杀死相应的进程,内核可能恢复正常,也可能处于一种不确定的状态,并进而导致内核的 Panic。
- Panic。 内核崩溃,即发生了严重且不可修复的错误, 如发生了非法的地址访问, 强制加载或卸载内核模块,以及硬件错误等等。
以上信息可用于初步分析内核崩溃的原因,内核态有三种出错情况,分别是 bug, oops和 panic。 bug 属于轻微错误, oops 代表某一用户进程出现错误,需要杀死用户进程。这时如果用户进程占用了某些信号锁,这些信号锁将永远不会得到释放,这会导致系统潜在的不稳定性。 panic 是严重错误,代表整个系统崩溃。 深入的分析需要使用更多的命令进行追踪和查找。
(4) crash常用命令
查看某个命令的详细介绍:help xxx
crash_arm64_v8> help
* files mod sbitmapq union
alias foreach mount search vm
ascii fuser net set vtop
bpf gdb p sig waitq
bt help ps struct whatis
btop ipcs pte swap wr
dev irq ptob sym q
dis kmem ptov sys
eval list rd task
exit log repeat timer
extend mach runq tree
crash_arm64_v8 version: 8.0.0++ gdb version: 10.2
For help on any command above, enter "help <command>".
For help on input options, enter "help input".
For help on output options, enter "help output".
crash_arm64_v8> help xxx
1.bt命令:
打印函数调用栈,displays a task’s kernel-stack backtrace 。
- -t: 显示符号信息
- -f: 显示栈的所有数据
- -l: 显示文件名和行号
- pid: 可以显示指定pid进程的backtrace
crash_arm64_v8> ps |grep UN
161 2 4 ffffff80b62a9e00 UN 0.0 0 0 [sprd-rotation/6]
162 2 4 ffffff80b62abc00 UN 0.0 0 0 [sprd-rotation/7]
228 2 0 ffffff80b52be900 UN 0.0 0 0 [slog-0-0]
730 2 0 ffffff8088c58000 UN 0.0 0 0 [RX_THREAD]
731 2 0 ffffff8088c58f00 UN 0.0 0 0 [RX_NET_QUEUE]
732 2 6 ffffff8088c5cb00 UN 0.0 0 0 [SPRDWL_TX_THREA]
847 2 6 ffffff80795bbc00 UN 0.0 0 0 [WCN_SIPC_TX_THR]
848 2 1 ffffff80795bda00 UN 0.0 0 0 [WCN_SIPC_TX_THR]
849 2 1 ffffff80795bad00 UN 0.0 0 0 [WCN_SIPC_TX_THR]
937 2 1 ffffff80795be900 UN 0.0 0 0 [native_hang_det]
crash_arm64_v8> bt 937
PID: 937 TASK: ffffff80795be900 CPU: 1 COMMAND: "native_hang_det"
#0 [ffffffc01765bc00] __switch_to at ffffffc0101022f4
#1 [ffffffc01765bc70] __schedule at ffffffc0115f7dc0
#2 [ffffffc01765bd00] schedule at ffffffc0115f8a3c
#3 [ffffffc01765bdb0] schedule_timeout at ffffffc0115fe91c
#4 [ffffffc01765be20] hang_detect_thread at ffffffc0109f86dc
#5 [ffffffc01765be80] kthread at ffffffc0101d8e30
crash_arm64_v8>
2.dis命令
显示反汇编。dis可以反汇编指定的地址,或者反汇编指定的函数名。
- 反汇编指定的函数
crash_arm64_v8> dis hang_detect_thread
0xffffffc0109f85ac <hang_detect_thread>: sub sp, sp, #0xa0
0xffffffc0109f85b0 <hang_detect_thread+4>: str x30, [x18], #8
0xffffffc0109f85b4 <hang_detect_thread+8>: stp x29, x30, [sp, #64]
...
0xffffffc0109f85cc <hang_detect_thread+32>: add x29, sp, #0x40
0xffffffc0109f85d0 <hang_detect_thread+36>: adrp x8, 0xffffffc011e83000 <vsock_dgram_ops+144>
0xffffffc0109f85d4 <hang_detect_thread+40>: ldr x8, [x8, #1672]
- 反汇编指定的地址
crash_arm64_v8> dis 0xffffffc0109f8610
0xffffffc0109f8610 <hang_detect_thread+100>: mov w3, #0x1 // #1
crash_arm64_v8> dis 0xffffffc0109f8610 6
0xffffffc0109f8610 <hang_detect_thread+100>: mov w3, #0x1 // #1
0xffffffc0109f8614 <hang_detect_thread+104>: mov x0, x19
0xffffffc0109f8618 <hang_detect_thread+108>: stp w8, w9, [sp, #16]
0xffffffc0109f861c <hang_detect_thread+112>: bl 0xffffffc0101ec1dc <__sched_setscheduler>
0xffffffc0109f8620 <hang_detect_thread+116>: adrp x0, 0xffffffc0125bb000 <static_ltree+24>
0xffffffc0109f8624 <hang_detect_thread+120>: add x0, x0, #0xe58
- -l 参数:显示反汇编和源码
crash_arm64_v8> dis -l hang_detect_thread
/home/bryan.he/sprdroid12_trunk_22b_ump518_bringup/bsp/kernel/kernel5.4/drivers/soc/sprd/debug/sysdump/native_hang_monitor.c: 599
0xffffffc0109f85ac <hang_detect_thread>: sub sp, sp, #0xa0
0xffffffc0109f85b0 <hang_detect_thread+4>: str x30, [x18], #8
....
0xffffffc0109f85cc <hang_detect_thread+32>: add x29, sp, #0x40
0xffffffc0109f85d0 <hang_detect_thread+36>: adrp x8, 0xffffffc011e83000 <vsock_dgram_ops+144>
...
0xffffffc0109f85dc <hang_detect_thread+48>: nop
0xffffffc0109f85e0 <hang_detect_thread+52>: mov w8, #0x1 // #1
/home/bryan.he/sprdroid12_trunk_22b_ump518_bringup/bsp/kernel/kernel5.4/kernel/sched/core.c: 5428
0xffffffc0109f85e4 <hang_detect_thread+56>: stp xzr, xzr, [sp, #40]
...
- -s参数:显示该函数的文件名和源代码
crash_arm64_v8> dis -s hang_detect_thread
FILE: /home/bryan.he/sprdroid12_trunk_22b_ump518_bringup/bsp/kernel/kernel5.4/drivers/soc/sprd/debug/sysdump/native_hang_monitor.c
LINE: 599
dis: hang_detect_thread: source code is not available
反汇编的其他用法:
crash_arm64_v8> dis ffffffffa021ba91 // 反汇编一条指令
crash_arm64_v8> dis -l probe_2093+497 10 // 反汇编从某个地址开始的10条指令
3.mod命令
加载模块符号表。
- -s: 加载模块符号表
- -d: 删除某个模块符号表
- -S: 加载某个目录的模块符号表
# mod // 查看模块
# mod -s module /path/to/module.ko // 加载模块
# sym symbol // 显示符号对应的模块源码,也可以用virtual address
4.sym命令
用来解析符号。
- -l: 显示所有的符号,等于用System.map。
crash_arm64_v8> sym -l
ffffffc010080000 (t) __efistub__text
ffffffc010080000 (t) _head
ffffffc010080000 (T) _text
ffffffc010080040 (t) pe_header
ffffffc010080044 (t) coff_header
ffffffc010080058 (t) optional_header
ffffffc010080070 (t) extra_header_fields
ffffffc0100800f8 (t) section_table
ffffffc010081000 (T) __exception_text_start
ffffffc010081000 (T) _stext
- -m module: 显示指定模块里的符号
crash_arm64_v8> sym -m rpmb
ffffffc0096b3000 MODULE START: rpmb
ffffffc0096b4000 (T) __cfi_check
ffffffc0096b4368 (") __cfi_check_fail
ffffffc0096b43a8 (T) rpmb_dev_unregister
ffffffc0096b446c (t) match_by_parent
ffffffc0096b44b0 (t) rpmb_dev_release
ffffffc0096b44f4 (T) rpmb_dev_register
ffffffc0096b46b0 (t) id_read
ffffffc0096b4750 (t) reliable_wr_cnt_show
ffffffc0096b4798 (t) type_show
ffffffc0096b4840 (T) rpmb_dev_put
ffffffc0096b4870 (T) rpmb_dev_get_by_type
ffffffc0096b4904 (t) match_by_type
ffffffc0096b4950 (T) rpmb_dev_get
ffffffc0096b4988 (T) rpmb_dev_find_device
ffffffc0096b49d8 (T) rpmb_dev_find_by_device
ffffffc0096b4a34 (T) rpmb_cmd_seq
ffffffc0096b4b5c (T) rpmb_cmd_req
- -q string: 查找符号
crash_arm64_v8> sym -q rpmb_ioctl
ffffffc010eb2134 (t) mmc_rpmb_ioctl
ffffffc010eb21b8 (t) mmc_rpmb_ioctl_compat
ffffffc0096b4f60 (t) rpmb_ioctl [rpmb]
方括号,[rpmb]告诉了该符号是属于模块rpmb的,左边是符号地址。
5.rd命令
读内存命令。
- -p: 读物理地址
crash_arm64_v8> rd -p 0x80008000 20
80008000: 0000000000009000 0000000000000000 ................
80008010: 0000000000000000 0000000000007448 ........Ht......
80008020: 0000000000007480 0000000000000000 .t..............
80008030: 0000000000000000 00000000000074c4 .........t......
80008040: 000000000000754c 0000000000000000 Lu..............
80008050: 0000000000000000 000000000000733c ........<s......
80008060: 0000000000007424 0000000000000000 $t..............
80008070: 0000000000000000 0000000000000000 ................
80008080: 0000000000000000 0000000000007630 ........0v......
80008090: 00000000000075dc 00000000000075e0 .u.......u......
- -u: 读用户虚拟地址
crash_arm64_v8> rd -u 0000007c6d862fa8
rd: invalid kernel virtual address: 3f type: "user pgd"
crash_arm64_v8> rd -u swapper
symbol not found: swapper
possible alternatives:
ffffffc01012b6e8 (T) set_swapper_pgd
ffffffc01242bba0 (b) swapper_pgdir_lock
crash_arm64_v8> rd -u swapper_spaces
rd: invalid user virtual address: ffffffc01227a9f0 type: "64-bit UVADDR"
神马情况??? 指定的地址不对?
- -d:显示十进制,-x显示十六进制。
crash_arm64_v8> rd -d ffffffc01012b6e8
ffffffc01012b6e8: -6215394105006979490
crash_arm64_v8> rd -x ffffffc01012b6e8
ffffffc01012b6e8: a9be7bfdf800865e
- -s: 显示符号
crash_arm64_v8> rd -s ffffffc01012b6e8
ffffffc01012b6e8: __efistub_$d.9+-6215394105006979767
- -32:显示32位宽的值
crash_arm64_v8> rd -32 ffffffc01012b6e8
ffffffc01012b6e8: f800865e ^...
- -64: 显示64位宽的值
crash_arm64_v8> rd -64 ffffffc01012b6e8
ffffffc01012b6e8: a9be7bfdf800865e ^....{..
- -a: 显示ASCII码
crash_arm64_v8> rd -a ffffffc01012b6e8
ffffffc01012b6e8: ^
crash_arm64_v8> rd -a ffffffc01012b6e8 10
ffffffc01012b6e8: ^
ffffffc01012b6ed: {
ffffffc01012b6f1: O
6.struct命令
显示数据结构的定义以及实际的值。
- 指定struct name: 显示内核中定义的数据结构。
crash_arm64_v8> struct vm_area_struct
struct vm_area_struct {
unsigned long vm_start;
unsigned long vm_end;
struct vm_area_struct *vm_next;
struct vm_area_struct *vm_prev;
struct rb_node vm_rb;
unsigned long rb_subtree_gap;
struct mm_struct *vm_mm;
pgprot_t vm_page_prot;
unsigned long vm_flags;
union {
struct {...} shared;
const char *anon_name;
};
struct list_head anon_vma_chain;
struct anon_vma *anon_vma;
const struct vm_operations_struct *vm_ops;
unsigned long vm_pgoff;
struct file *vm_file;
void *vm_private_data;
atomic_long_t swap_readahead_info;
struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
u64 android_kabi_reserved1;
u64 android_kabi_reserved2;
u64 android_kabi_reserved3;
u64 android_kabi_reserved4;
u64 android_vendor_data1;
}
SIZE: 232
- 指定.member: 显示结构体中的某个成员。
crash_arm64_v8> struct vm_area_struct.vm_mm
struct vm_area_struct {
[64] struct mm_struct *vm_mm;
}
- 同时指定struct name和address地址:显示该数据结构在该地址的值。
crash_arm64_v8> struct -x vm_area_struct ffffffc0116150fc
struct vm_area_struct {
vm_start = 0x17bd992417b92167,
vm_end = 0x17ac55b317bda103,
vm_next = 0x17ac8b1517ac8afc,
vm_prev = 0x17b74cb617b745eb,
vm_rb = {
__rb_parent_color = 0x17b760eb17b75e34,
rb_right = 0x17b789f617b78552,
rb_left = 0x17b795c917b78b40
},
rb_subtree_gap = 0x17b7adfa17b7aa00,
vm_mm = 0x17b89a1d17b7b08c,
vm_page_prot = {
pgprot = 0x17ba373317b992cf
},
vm_flags = 0x17ba376517ba3739,
{
shared = {
rb = {
__rb_parent_color = 0x17ba8ef017ba6d60,
rb_right = 0x17baeb5e17bada48,
rb_left = 0x17bdc7c217bdc797
},
rb_subtree_last = 0x17bd017e17bdc93b
},
anon_name = 0x17ba8ef017ba6d60 <error: Cannot access memory at address 0x17ba8ef017ba6d60>
},
anon_vma_chain = {
next = 0x17bdc99217bdc955,
prev = 0x17bdcbb217bdca57
},
anon_vma = 0x17bdcc0717bdcbf3,
...
- 使用-o参数: 显示每个成员在数据结构中的偏移(默认十进制,-x指定16进制)
crash_arm64_v8> struct -o vm_area_struct
struct vm_area_struct {
[0] unsigned long vm_start;
[8] unsigned long vm_end;
....
[80] unsigned long vm_flags;
union {
[88] struct {...} shared;
[88] const char *anon_name;
};
[120] struct list_head anon_vma_chain;
...
}
SIZE: 232
crash_arm64_v8> struct -o -x vm_area_struct #十六进制显示偏移
struct vm_area_struct {
[0x0] unsigned long vm_start;
[0x8] unsigned long vm_end;
[0x10] struct vm_area_struct *vm_next;
union {
[0x58] struct {...} shared;
[0x58] const char *anon_name;
};
[0x78] struct list_head anon_vma_chain;
....
}
SIZE: 0xe8
6.p命令
用来打印一个表达式或者符号的值。
- 指定symbol: 打印一个内核的符号的值,通常是export出来的全局变量等。
crash_arm64_v8> p pid
pid = $1 =
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
crash_arm64_v8> p init
init = $2 =
{int (void)} 0xffffffc0120e4348 <init>
crash_arm64_v8> p init_mm
init_mm = $3 = {
{
mmap = 0x0,
mm_rb = {
rb_node = 0x0
},
vmacache_seqnum = 0,
get_unmapped_area = 0x0,
mmap_base = 0,
mmap_legacy_base = 0,
task_size = 0,
highest_vm_end = 0,
pgd = 0xffffffc01206a000,
....
- 通过指定“symbol : cpuid”的方式打印某个cpu上pre-cpu变量的值。
crash_arm64_v8> p irq_stat:2
per_cpu(irq_stat, 2) = $5 = {
__softirq_pending = 0
}
crash_arm64_v8> p irq_stat:7
per_cpu(irq_stat, 7) = $6 = {
__softirq_pending = 0
}
7.irq命令
显示中断相关的信息。
- index: 显示某个中断相关的信息。
crash_arm64_v8> irq 16
IRQ IRQ_DESC/_DATA IRQACTION NAME
16 ffffff80bac33800 ffffff80b7fd8980 "sprd_serial1"
crash_arm64_v8> dis ffffff80b7fd8980
0xffffff80b7fd8980 <__efistub_$d.9+-546668967829>: add w20, w25, #0x88a, lsl #12
crash_arm64_v8> rd -s ffffff80b7fd8980
ffffff80b7fd8980: __typeid__ZTSF9irqreturniPvE_global_addr+72
crash_arm64_v8> rd -a ffffff80b7fd8980
ffffff80b7fd8980: 4+b
- -b: 显示中断下半部
SOFTIRQ_VEC ACTION
[0] ffffffc011623c54 <(null)>
[1] ffffffc011623c60 <(null)>
[2] ffffffc011623c6c <(null)>
[3] ffffffc011623c70 <(null)>
[4] ffffffc011623c68 <(null)>
[6] ffffffc011623c50 <__typeid__ZTSFvP14softirq_actionE_global_addr>
[7] ffffffc011623c58 <(null)>
[8] ffffffc011623c64 <(null)>
[9] ffffffc011623c5c <(null)>
- -a:显示中断亲和性
crash_arm64_v8> irq 12 -a
IRQ NAME AFFINITY
1 IPI 0-7
2 IPI 0-7
3 IPI 0-7
4 IPI 0-7
5 IPI 0-7
6 IPI 0-7
7 IPI 0-7
9 vgic 0-7
10 timer@64470000 1
12 arch_timer 0-7
13 kvm guest vtimer 0-7
16 sprd_serial1 0
18 200d0000.i2c 0
19 ....
25 20120000.spi 0
26 20150000.spi 0
27 mmc0 0
28 mmc1 0
29 ufshcd 0
30 VSP 0
31 JPG 0
50 23100000.gpu,23100000.gpu,23100000.gpu 0
51 ...
101 fts_ts 0-7
102 head_aud_det_int_all 0-7
- s: 显示系统中断信息
crash_arm64_v8> irq -s
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
1: 141083 147923 115758 98548 71009 54827 33703 26694 GICv3 IPI
2: 541 482 467 456 493 447 7423 5278 GICv3 IPI
3: 1 1 1 1 1 1 1 0 GICv3 IPI
4: 0 0 0 0 0 0 0 0 GICv3 IPI
5: 8409 7458 9391 12822 14395 14664 6402 7579 GICv3 IPI
6: 29650 24306 20710 19168 11469 9064 26445 16654 GICv3 IPI
7: 0 0 0 0 0 0 0 0 GICv3 IPI
9: 0 0 0 0 0 0 0 0 GICv3 vgic
10: 112944 67639 48583 45316 58016 42869 12659 9506 GICv3 timer@64470000
12: 183638 120837 99488 97025 89706 71386 65808 60780 GICv3 arch_timer
13: 0 0 0 0 0 0 0 0 GICv3 kvm guest vtimer
16: 1 0 0 0 0 0 0 0 GICv3 sprd_serial1
...
8.task命令
进程中task_struct和thread_info数据结构的值
crash_arm64_v8> task
PID: -1 TASK: ffffff801e604b00 CPU: -1 COMMAND: "bash"
struct task_struct {
thread_info = {
flags = 18446744073709551615,
addr_limit = 18446744073709551615,
ttbr0 = 18446744073709551615,
{
preempt_count = 18446744073709551615,
preempt = {
count = 4294967295,
need_resched = 4294967295
}
},
shadow_call_stack = 0xffffffffffffffff
},
state = -1,
....
9.vm命令
显示指定进程(指定PID)的相关内存的信息。
crash_arm64_v8> vm 305
PID: 305 TASK: ffffff80b97e0f00 CPU: 1 COMMAND: "logd.klogd"
MM PGD RSS TOTAL_VM
ffffff80b9fe3840 ffffff80b9fdc000 6488k 10919116k
VMA START END FLAGS FILE
ffffff80b9875050 5716435000 5716444000 871 /first_stage_ramdisk/system/bin/logd
ffffff80b98754d8 5716444000 57164e6000 875 /first_stage_ramdisk/system/bin/logd
ffffff80b9874570 57164e6000 57164e8000 100871 /first_stage_ramdisk/system/bin/logd
ffffff80b98753f0 57164e8000 57164e9000 100873 /first_stage_ramdisk/system/bin/logd
ffffff803a9e6828 6f4a239000 6f4a7fc000 70
ffffff803a9e69f8 6f4a7fc000 6f4a7fe000 100073
ffffff803a9e7960 6f4a7fe000 6f4b239000 70
ffffff803aa4a3a0 6f4b239000 6f4b23a000 200070
ffffff803aa4a000 6f4b23a000 6f4b336000 200073
ffffff803aa4bc18 6f4b336000 6f4b337000 200070
- -p: 显示虚拟地址和物理地址
crash_arm64_v8> vm 305 -p
PID: 305 TASK: ffffff80b97e0f00 CPU: 1 COMMAND: "logd.klogd"
MM PGD RSS TOTAL_VM
ffffff80b9fe3840 ffffff80b9fdc000 6488k 10919116k
VMA START END FLAGS FILE
ffffff80b9875050 5716435000 5716444000 871 /first_stage_ramdisk/system/bin/logd
VIRTUAL PHYSICAL
5716435000 131e4f000
5716436000 131e50000
...
5716443000 131e64000
VMA START END FLAGS FILE
ffffff80b98754d8 5716444000 57164e6000 875 /first_stage_ramdisk/system/bin/logd
VIRTUAL PHYSICAL
5716444000 1310b4000
...
5716478000 131172000
5716479000 FILE: /first_stage_ramdisk/system/bin/logd OFFSET: 44000
571647a000 131d6c000
571647b000 131eb5000
571647c000 131eb4000
571647d000 131eb3000
571647e000 1320aa000
571647f000 1320a9000
...
5716485000 131ea4000
- -m: 显示mm_struct数据结构的值
crash_arm64_v8> vm 305 -m
PID: 305 TASK: ffffff80b97e0f00 CPU: 1 COMMAND: "logd.klogd"
struct mm_struct {
{
mmap = 0xffffff80b9875050,
mm_rb = {
rb_node = 0xffffff80b8a257b0
},
vmacache_seqnum = 289,
get_unmapped_area = 0xffffffc011620c2c,
mmap_base = 489202118656,
mmap_legacy_base = 0,
task_size = 549755813888,
highest_vm_end = 549578174464,
pgd = 0xffffff80b9fdc000,
membarrier_state = {
counter = 0
},
mm_users = {
counter = 8
},
mm_count = {
counter = 1
},
pgtables_bytes = {
counter = 258048
},
map_count = 264,
page_table_lock = {
{
rlock = {
raw_lock = {
{
val = {
counter = 0
},
- -v: dump该进程所有vm_area_struct数据结构的值。
crash_arm64_v8> vm 305 -v
PID: 305 TASK: ffffff80b97e0f00 CPU: 1 COMMAND: "logd.klogd"
struct vm_area_struct {
vm_start = 374035664896,
vm_end = 374035726336,
vm_next = 0xffffff80b98754d8,
vm_prev = 0x0,
vm_rb = {
__rb_parent_color = 18446743527066391801,
rb_right = 0x0,
rb_left = 0x0
},
rb_subtree_gap = 374035664896,
vm_mm = 0xffffff80b9fe3840,
vm_page_prot = {
pgprot = 27021597764227027
},
vm_flags = 2161,
{
shared = {
rb = {
__rb_parent_color = 18446743527066391857,
rb_right = 0x0,
rb_left = 0x0
},
rb_subtree_last = 14
},
anon_name = 0xffffff80b9875531 ""
},
- -f 1234: 数字1234在vm_flags的对应比特位
crash_arm64_v8> vm 305 -f 1234
1234: (EXEC|MAYREAD|MAYWRITE|NOHUGEPAGE|EXECUTABLE)
crash_arm64_v8> vm 305 -f 1239
1239: (READ|SHARED|MAYREAD|MAYWRITE|NOHUGEPAGE|EXECUTABLE)
10.kmem命令
当前(转存快照时)系统内存的信息。
- -i: 显示系统内存使用情况。
crash_arm64_v8> kmem -i
PAGES TOTAL PERCENTAGE
TOTAL MEM 704134 2.7 GB ----
FREE 12576 49.1 MB 1% of TOTAL MEM
USED 691558 2.6 GB 98% of TOTAL MEM
SHARED 0 0 0% of TOTAL MEM
BUFFERS 1210 4.7 MB 0% of TOTAL MEM
CACHED 379164 1.4 GB 53% of TOTAL MEM
SLAB 57415 224.3 MB 8% of TOTAL MEM
TOTAL HUGE 0 0 ----
HUGE FREE 0 0 0% of TOTAL HUGE
TOTAL SWAP 436562 1.7 GB ----
SWAP USED 32256 126 MB 7% of TOTAL SWAP
SWAP FREE 404306 1.5 GB 92% of TOTAL SWAP
COMMIT LIMIT 788629 3 GB ----
COMMITTED 17644777 67.3 GB 2237% of TOTAL LIMIT
-
-v: 显示系统vmalloc使用情况。
-
V:显示系统vm_stat情况
-
-z: 显示每个zone的情况
crash_arm64_v8> kmem -z
NODE: 0 ZONE: 0 ADDR: ffffffc01237d100 NAME: "DMA32"
SIZE: 524288 PRESENT: 524272 MIN/LOW/HIGH: 1045/6456/7372
VM_STAT:
NR_FREE_PAGES: 9115
NR_ZONE_INACTIVE_ANON: 2459
NR_ZONE_ACTIVE_ANON: 90029
NR_ZONE_INACTIVE_FILE: 218441
NR_ZONE_ACTIVE_FILE: 21419
NR_ZONE_UNEVICTABLE: 5570
NR_ZONE_WRITE_PENDING: 36
NR_MLOCK: 5570
NR_PAGETABLE: 11919
NR_KERNEL_STACK_KB: 17456
NR_KERNEL_SCS_BYTES: 4460544
NR_BOUNCE: 0
NR_ZSPAGES: 3867
NR_FREE_CMA_PAGES: 0
NODE: 0 ZONE: 1 ADDR: ffffffc01237d780 NAME: "Normal"
SIZE: 262143 MIN/LOW/HIGH: 560/3462/3953
VM_STAT:
NR_FREE_PAGES: 3461
NR_ZONE_INACTIVE_ANON: 15847
NR_ZONE_ACTIVE_ANON: 28576
NR_ZONE_INACTIVE_FILE: 61498
NR_ZONE_ACTIVE_FILE: 52658
NR_ZONE_UNEVICTABLE: 20188
NR_ZONE_WRITE_PENDING: 28
NR_MLOCK: 20188
NR_PAGETABLE: 2998
NR_KERNEL_STACK_KB: 7808
NR_KERNEL_SCS_BYTES: 2007040
NR_BOUNCE: 0
NR_ZSPAGES: 3522
NR_FREE_CMA_PAGES: 0
NODE: 0 ZONE: 2 ADDR: ffffffc01237de00 NAME: "Movable"
[unpopulated]
- -s: 显示每个slab的情况
- -p: 显示每个页面的情况
crash_arm64_v8> kmem -p
PAGE PHYSICAL MAPPING INDEX CNT FLAGS
ffffffff0de00000 400000000 0 0 0 0
ffffffff0de00040 400001000 0 0 0 0
ffffffff0de00080 400002000 0 0 0 0
ffffffff0de000c0 400003000 0 0 0 0
ffffffff0de00100 400004000 0 0 0 0
ffffffff0de00140 400005000 0 0 0 0
ffffffff0de00180 400006000 0 0 0 0
ffffffff0de001c0 400007000 0 0 0 0
ffffffff0de00200 400008000 0 0 0 0
...
- -g: 显示struct page里面flag的比特位
crash_arm64_v8> kmem -g
PAGE-FLAG BIT VALUE
PG_locked 0 000001
PG_referenced 1 000002
PG_uptodate 2 000004
PG_dirty 3 000008
PG_lru 4 000010
PG_active 5 000020
PG_workingset 6 000040
PG_waiters 7 000080
PG_error 8 000100
PG_slab 9 000200
PG_owner_priv_1 10 000400
PG_arch_1 11 000800
PG_reserved 12 001000
PG_private 13 002000
PG_private_2 14 004000
PG_writeback 15 008000
PG_head 16 010000
PG_mappedtodisk 17 020000
PG_reclaim 18 040000
PG_swapbacked 19 080000
PG_unevictable 20 100000
PG_mlocked 21 200000
PG_checked 10 000400
PG_swapcache 10 000400
PG_fscache 14 004000
PG_pinned 10 000400
PG_savepinned 3 000008
PG_foreign 10 000400
PG_xen_remapped 10 000400
PG_slob_free 13 002000
PG_double_map 14 004000
PG_isolated 18 040000
11.list命令
用来遍历链表,并且可以打印链表中成员的值。
- -h : 指定链表头list_head的地址
- -s: 用来打印链表成员的值
例如,mutex_waiter结构体定义:
struct mutex_waiter {
struct list_head list;
struct task_struct *task;
...
}
crash_arm64_v8> struct mutex.owner,count,wait_list 0xffffffc0126ddf4c
owner = {
counter = .2
},
wait_list = {
next = 0xffffffc0126ddfdd,//下一个元素头
prev = 0xffffffc0126ddf12
}
crash_arm64_v8> list -s mutex_waiter.task -h 0xffffffc0126ddfdd
ffffffc0126ddfdd
task = 0xffffffc0126d1124
ffffffc0626ddfdd
task = 0xffffffc0126d1124
...
task = ...