crash工具的常用命令

作者 by adtxl / 2022-08-30 / 暂无评论 / 257 个足迹

众所周知,Linux系统可能会因为异常指令、访问无效内存地址、死锁(soft lockup)等原因崩溃。而系统崩溃后,我们可以通过kdump将当时的内存信息dump下来并生成一个转储文件vmcore,同时,crash工具可以帮助我们分析vmcore文件并找到问题所在。

1. 下载与安装

1)从官方网站下载crash源代码;

git clone git://github.com/crash-utility/crash.git

2)编译前确保必要的组件(ncurese和zlib);

sudo apt-get install libncurses5-dev
sudo apt-get install zlib1g-dev

3)解压后编译ARM 32bit的crash:

cd crash-7.2.8
make target=ARM64

4)如果是ARM 64bit的,则是:

cd crash-7.2.8
make target=ARM64

5)编译后会在当前目录下生成crash,可以将多余的符号去除:

strip -s crash

2. 常用命令

牢记一件事,详细的命令使用可以使用help命令来查看。
image.png

image74e7765a086662d0.png

image0aed03c4fb418215.png

imagead770f0774b2daa3.png

下面不会介绍每个命令的详细选项,一下子也很难全记住,只记录一些常用的

2.1 bt命令--堆栈使用以及问题定位分析

image721cc65d18c3eace.png

image996ba049656592d3.png

image8a077e0ca343bc98.png

2.2 log命令--系统日志定位分析

log 打印内核日志

log -t 不包含时间片的模式打印内核日志

log -d 对使用 dev_printk() 打印 SUBSYSTEM/DEVICE 信息

log -m 包含打印等级的模式打印内核日志

log -a 打印 内核 audit buffer 中的 audit 日志

2.3 ps命令--进程信息定位分析

image46302cb82b1e8bf3.png

image070281c1f80e8259.png

imagef30fce0922ae949a.png

更多选项如下:

ps 打印内核崩溃时所有进程的状态信息

ps pid 打印内核奔溃时指定进程的状态信息

ps task 打印内核崩溃时进程描述符对应的进程状态信息

ps command 打印内核崩溃时进程命令对应的进程状态信息

ps -k 只输出内核崩溃时内核线程的状态信息

ps -u 只输出内核崩溃时用户进程的进程状态信息

ps -G 只输出内核崩溃时线程组的 Leader 线程状态信息

ps -y policy 只输出内核崩溃时符合特定调度策略的进程状态

ps -s 输出内核崩溃时进程的内核栈地址

ps -p 输出内核崩溃时进程的父子关系

ps -c 打印内核崩溃时某进程的所有子进程

ps -t 打印内核崩溃时进程时间相关的信息

ps -l 打印内核崩溃时进程最后运行的时间戳

ps -m 打印内核崩溃时进程最后运行的时间日期

ps -C cpus [m/l] 打印内核崩溃时指定 CPU 上进程时间信息

ps -g 打印内核崩溃时包含指定线程的线程组信息

ps -r 打印内核崩溃时进程的资源限制信息

ps -a 打印内核崩溃时进程的参数和环境变量

ps -S 打印内核崩溃时不同进程状态的数量信息

ps -A 打印内核崩溃时各个 CPU 上正在运行的进程状态

2.4 struct/union命令--数据结构信息分析

CRASH 获得 struct task_struct 的方法:

image77f995069282b1cb.png

CRASH 获得 union thread_info 的方法:
如上图,可以看到进程 1756 的 struct task_struct 结构体的数据全部打印出来,可以结合 struct task_struct 结构的定义一一获得所需的数据。另外有些结构体或联合体不能简单的通过上面的方法获得,其需要结合一些原理进行获得,例如 union thread_union,其在不同架构中的布局不同,但大体可以描述为下图:

image63eed755d4f0e4a3.png

union thread_union 联合体与进程的内核堆栈绑定在一起,位于内核堆栈的顶部,然后进程的内核堆栈通过 struct task_struct 的 stack 成员进行指定,例如在进程 1756 中,struct task_struct 的 task 成员的值是 0xffffff800e878000, 那么该地址就是进程内核堆栈的地址,在通过转换就可以知道该地址也就是 union thread_info 的地址,因此:

imagee8730ed3a5df42db.png

imageb377ea5df227fea6.png

更多选项:


struct struct_name 获得指定结构体在内核中的定义

struct struct_name <addr> 获得指定结构体的内容

struct struct_name.member <addr> 获得指定结构体中指定 member 数据

struct struct_name -o 获得数据结构各成员在结构体中的偏移

struct struct_name <addr> -r 获得结构体原始数据

struct struct_name <addr> -x/-d 以十六进制/十进制方式输出结构体内容

struct struct_name <addr> -p 打印结构体中指针的类型

struct struct_name symobl 查看全局 symbol 的结构体内容

struct struct_name symbol:cpuspec 打印指定 PERCPU 在指定 CPU 上结构体内容

CRASH union 命令


union union_name 获得指定联合体体在内核中的定义

union union_name <addr> 获得指定联合体的内容

union union_name.member <addr> 获得指定联合体中指定 member 数据

union union_name -o 获得联合体各成员在联合体中的偏移

union union_name <addr> -r 获得结构体原始数据

union union_name <addr> -x/-d 以十六进制/十进制方式输出联合体体内容

union union_name <addr> -p 打印联合体中指针的类型

union union_name symobl 查看全局 symbol 的结构体内容

union_union symbol:cpuspec 打印指定 PERCPU 在指定 CPU 上联合体内容

2.5 物理内存相关命令

CRASH ptob 命令

ptob <pfn> 将物理页帧号转化成物理地址

CRASH ptov 命令

ptov <address> 将物理内存转换成虚拟内存

ptov offset:cpuspec 通过 PERCPU 变量物理地址获得在指定 CPU 上的虚拟地址

image2445b3e5d69b8747.png

2.6 页表相关的命令

pte <conteents> 解码 PTE 内容

CRASH 提供的 pte 命令将一个十六进制的 PTE 页表内容进行解码,解码之后可以获得 PTE 对应的物理页以及 PTE 页表的标志。如果 PTE 对应的物理页表位于 SWAP 中,那么 CRASH 将输出物理页在 SWAP 中偏移以及 SWAP 的设备名.

imageb2e055bf9c56f6e8.png

2.6 虚拟内存相关命令

用户进程的地址空间有多个区域组成,这些区域称为虚拟区域 VMA。每个 VMA 维护了一段虚拟内存,并用于指定的任务,例如用于存储进程代码段的 VMA、用于存储进程数据的 VMA 等。CRASH 提供了 vm 命令可以获得当前 CPU 上运行进程的用户进程虚拟区域信息

image4b0edde0a306f48d.png

CRASH vm 命令将展示一下信息:

  • PID 进程 ID
  • TASK 进程描述符 struct task_struct 的虚拟地址
  • CPU 当前 CPU ID
  • COMMAND 当前进程运行的命令名字命令名字
  • MM 进程地址空间描述符 struct mm_struct 的地址
  • PGD 进程 PGD 页表入口的地址
  • RSS 进程使用物理内存的数量
  • TOTAL_VM 进程占用的虚拟内存数量
  • VMA 虚拟区域的描述符 struct vm_area_struct 的虚拟地址
  • START 虚拟区域的起始地址
  • END 虚拟区域的结束地址
  • FLAGS 虚拟区域标志
  • FILE 虚拟区域映射的文件

image78cb8f9dbc47dae2.png

imageef366ca0cf6ad3cc.png

更多选项:

vm pid 通过进程 ID 获得指定进程用户空间的虚拟区域信息

vm taskp 通过进程描述符获得进程用户空间虚拟区域信息

vm -p 打印进程所有虚拟区域到物理内存的映射关系

vm -P 打印进程的指定虚拟区域到物理内存的映射关系

vm -m 打印用户进程的地址描述符 struct mm_struct 内容

vm -R 在 “vm” 命令输出中查找指定内容

vm -R filename 查找映射了指定文件的 VMA 信息

vm -R vaddr 查找包含指定虚拟地址的 VMA 信息

vm -R flags 查找 VMA Flags 为指定值的 VMA 信息

vm -R phys 查找映射指定物理地址的虚拟地址信息

vm -R vmap 查找 vmap 的虚拟区域信息

vm -v 打印进程所有 VMA 描述符内容

vm -f 解码虚拟区域标志

vm -d/-x 输出内容格式控制

2.7 内存读写

CRASH rd 命令

rd -p <address> 读取物理地址的内容

rd -u <addresss> 读取虚拟地址的内容

[rd [-dDx][-8][-16][-32][-64][-a] <address> 格式化输出物理地址内容](#E0A05)

rd -s <address> 读取物理内存中符号引用

rd -S <address> 读取物理内存中 SLAB cache 的引用

rd -SS <address> 读取物理内存中 SLAB cache 的引用和内容

rd <symbol> 读取符号所在物理内存的内容

rd <address> count 读取一定数量物理内存的内容

rd <address> -o offset 读取起始物理地址偏移处的内容

rd <addr0> -e <addr1> 读取物理内存 addr0 到 addr1 之间的内容

CRASH wr 命令

wr -p <address> value 向物理地址写入指定值

wr -u <address> value 向用户虚拟地址写入指定值

wr -k <address> value 向内核虚拟地址写入指定值

wr <symbol> value 向符号写入指定值

wr <address> [-8][-16][-32][-64] value 向物理地址写入格式化值

2.8 系统内存统计信息

当使用 CRASH 分析内核转储文件时,系统的内存使用率对问题的定位提供了很多有用的信息,例如当前系统物理内存使用情况、物理页使用情况、SLAB 缓存数据情况等,CRASH 提供了 kmem 命令用于获得系统内存使用信息,如下 :

imageb143729d5b199dcb.png

CRASH kmem 命令

kmem -i 打印系统物理内存使用情况

kmem -f 打印 ZONE 内 free_area 的信息

kmem -v 打印 VMALLOC 分配器分配的内存信息

kmem -V 打印系统 vm_state 表内容

kmem -V 打印系统 vm_node_state 表内容

kmem -V 打印系统 vm_numa_state 表内容

kmem -V 打印系统 vm_event_state 表内容

kmem -n 打印内存模型下的物理内存信息

kmem -z 打印所有 Zone 的统计信息

kmem -h 打印大页内存信息

2.9 打印表达式的值

CRASH 提供的 “p symbol” 命令可以打印 symbol 的值,这里值得注意的是如果 symbol 的范围,目前支持全局导出的 symbol。具体系统导出的 symobl 可以在内核源码的生成的 System.map 里获得。”p symbol” 命令执行之后将会输出 symbol 的值,如果 symbol 是一个变量,那么就输出变量的值; 如果 symbol 是一个结构体或联合体,那么就输出其内容; 如果 symbol 是一个 PERCPU 变量,那么将打印 PERCPU 变量在所有 CPU 上的值。

p symobl 打印符号的值
px/pd 以十六进制/十进制方式输出

imageacd5798b66737367.png

p symbol:cpuspec 打印 PERCPU 变量的值

CRASH 提供的 p symbol:cpuspec 命令可以用于打印 PERCPU 变量,众所周知 PERCPU 变量在每个 CPU 上都有一个副本,那么该命令正好可以打印 PERCPU 变量在指定 CPU 上的值,也可以打印所有 CPU 上的值,其命令格式如下:

imageff878484b9085424.png

2.10 链表操作

CRASH list 命令

list structure.member <start> 通过单链表成员起始地址打印单链表所有成员的地址

list [-o] offset <start> 通过单链表成员起始地址打印单链表所有成员的地址

list structure.member -H <start> 通过双链表表头起始地址打印双链表成员的地址

list [-o] offset -H <start> 通过双链表表头起始地址打印双链表成员的地址

list structure.member -h <start> 通过双链表成员起始地址打印双链表所有成员的地址

list [-o] offset -h <start> 通过双链表成员起始地址打印双链表所有成员的地址

list -s/-S struct 打印链表成员的内容

list -r 逆序输出链表

2.11 模块分析

CRASH mod 命令

mod 打印内核转储时系统中模块的信息

mod -s <module> [objectfile] 加载模块的符号表和 debuginfo

mod -S [directory] 从指定路径加载模块的目标文件

mod -d <module> 移除模块的符号表和 debuginfo

mod -D 移除所有模块的目标文件

lsmod 查看内核崩溃是加载的模块

参考文献:
1.内核核心转储: Kdump with kexec and crash
2.CRASH安装和调试

独特见解