1. rd 命令
用法:
8 位宽:rd -8
16位宽:rd -16
32位宽:rd -32
64位宽:rd -64
- 将内存中的数据转换为可以识别的内存符号、函数指针、slab对象、文件指针等。
- rd -s
- 如果想得到详细的转换,那么可以用rd -S或者rd -SS
- 将读到的区域dump到一个文件
- 设置结束地址,如果不设置长度,那么一直读到设置的结束地址
- 从指定偏移处开始读区,这个偏移是基于传入地址
crash> bt 50486
PID: 50486 TASK: ffff881171480c80 CPU: 18 COMMAND: "ps"
#0 [ffff8810d1d9bce0] schedule at ffffffff815f353d
#1 [ffff8810d1d9bd40] rwsem_down_read_failed at ffffffff815f61ea
#2 [ffff8810d1d9bd98] call_rwsem_down_read_failed at ffffffff813271d4
#3 [ffff8810d1d9bde0] down_read at ffffffff815f58b3
#4 [ffff8810d1d9bde8] proc_pid_cmdline_read at ffffffff8126c5e8
#5 [ffff8810d1d9be70] __vfs_read at ffffffff81202fd6
#6 [ffff8810d1d9bee8] vfs_read at ffffffff8120360a
#7 [ffff8810d1d9bf18] sys_read at ffffffff81204382
crash> rd ffff8810d1d9bd98 -e ffff8810d1d9bde0
ffff8810d1d9bd98: ffffffff813271d4 ffff880000000000 .q2.............
ffff8810d1d9bda8: 0000000000024200 ffff88203f0226f0 .B.......&.? ...
ffff8810d1d9bdb8: 00000000004037ae 0000000000000078 .7@.....x.......
ffff8810d1d9bdc8: 0000000000000000 0000000000000020 ........ .......
ffff8810d1d9bdd8: ffff88203c8fc0a8
2. set 命令
set 命令行应用广泛,可以指定进程的上下文。如果不指定,就显示kernel panic 时正在运行的进程。
crash> set
PID: 208789
COMMAND: "CPU 8/KVM"
TASK: ffff8803472a90d0 [THREAD_INFO: ffff88031e614000]
CPU: 54
STATE: TASK_RUNNING (PANIC)
通过 -c 可以指定cpu,以显示cpu上运行的进程,-p 选项可以显示kernel panic时运行的进程。
crash> set -c 3
PID: 59023
COMMAND: "CPU 35/KVM"
TASK: ffff880262d990d0 [THREAD_INFO: ffff882f4cbc8000]
CPU: 3
STATE: TASK_INTERRUPTIBLE (ACTIVE)
crash> set -p
PID: 208789
COMMAND: "CPU 8/KVM"
TASK: ffff8803472a90d0 [THREAD_INFO: ffff88031e614000]
CPU: 54
STATE: TASK_RUNNING (PANIC)
有时crash 会输出大量信息。下述命令可以禁止滚动
crash> set scroll off
scroll: off (/usr/bin/less)
也可以用以下命令
crash> sf
scroll: off (/usr/bin/less)
crash>
sf是别名,是set scroll off 的简写,默认别名可以使用alias查看。
crash> alias
ORIGIN ALIAS COMMAND
builtin man help
builtin ? help
builtin quit q
builtin sf set scroll off
builtin sn set scroll on
builtin hex set radix 16
builtin dec set radix 10
builtin g gdb
builtin px p -x
builtin pd p -d
builtin for foreach
builtin size *
builtin dmesg log
builtin lsmod mod
builtin last ps -l
3. h 命令
h 命令可以显示输入过的命令
crash> h
[1] set
[2] set scroll off
[3] sf
[4] alias
crash>
4. bt 命令
bt 命令可以输出进程的backtrace,下面命令可以显示所有进程的backtrace。
crash> foreach bt -tf
-t 选项表示显示栈中所有的文本符号(text symbol)
说明:函数在被定义为inline,因此在转储的backtrace不会显示call 对应的函数
crash> bt 3
PID: 3 TASK: ffff880167380000 CPU: 0 COMMAND: "ksoftirqd/0"
#0 [ffff88016738be20] __schedule at ffffffff816c201b
#1 [ffff88016738be88] schedule at ffffffff816c25d9
#2 [ffff88016738be98] smpboot_thread_fn at ffffffff810bef72
#3 [ffff88016738bec8] kthread at ffffffff810b65f1
#4 [ffff88016738bf50] ret_from_fork at ffffffff816ce0f7
crash> bt -t 3
PID: 3 TASK: ffff880167380000 CPU: 0 COMMAND: "ksoftirqd/0"
START: __schedule at ffffffff816c201b
[ffff88016738be88] schedule at ffffffff816c25d9
[ffff88016738be98] smpboot_thread_fn at ffffffff810bef72
[ffff88016738beb8] smpboot_thread_fn at ffffffff810bee90
[ffff88016738bec8] kthread at ffffffff810b65f1
[ffff88016738bf30] kthread at ffffffff810b6520
[ffff88016738bf50] ret_from_fork at ffffffff816ce0f7
[ffff88016738bf80] kthread at ffffffff810b6520
-f 选项能显示栈帧内所有栈数据,该选项可以方便的查看函数参数
-l 显示文件名和行号
crash> bt -l 3
PID: 3 TASK: ffff880167380000 CPU: 0 COMMAND: "ksoftirqd/0"
#0 [ffff88016738be20] __schedule at ffffffff816c201b
/data/rpmbuild/kernel3/5648486/BUILD/kernel-3.10.0/kernel-3.10.0/kernel/sched/core.c: 2562
#1 [ffff88016738be88] schedule at ffffffff816c25d9
/data/rpmbuild/kernel3/5648486/BUILD/kernel-3.10.0/kernel-3.10.0/kernel/sched/core.c: 3656
#2 [ffff88016738be98] smpboot_thread_fn at ffffffff810bef72
/data/rpmbuild/kernel3/5648486/BUILD/kernel-3.10.0/kernel-3.10.0/kernel/smpboot.c: 158
#3 [ffff88016738bec8] kthread at ffffffff810b65f1
/data/rpmbuild/kernel3/5648486/BUILD/kernel-3.10.0/kernel-3.10.0/kernel/kthread.c: 202
#4 [ffff88016738bf50] ret_from_fork at ffffffff816ce0f7
/data/rpmbuild/kernel3/5648486/BUILD/kernel-3.10.0/kernel-3.10.0/arch/x86/kernel/entry_64.S: 370
-a 选项只显示当前进程,但是在运行的系统中无法显示当前进程。
4. dev命令
dev命令可以显示字符设备列表,
-p 选项显示pci 数据,其内容与lspci 基本相同。
-i 选项可以显示IO端口和IO设备与内存映射,与下面命令基本相同
cat /proc/ioports
cat /proc/iomem
crash> dev -p
ROOT BUS BUSNAME
ffff8830a5a97400 0000:00
PCI DEV DO:BU:SL.FN CLASS PCI_ID TYPE
ffff8830a6245000 0000:00:00.0 0600 8086:2020 ROOT_PORT
ffff8830a6243000 0000:00:04.0 0880 8086:2021 RC_END
ffff8830a6242000 0000:00:04.1 0880 8086:2021 RC_END
ffff8830a6241000 0000:00:04.2 0880 8086:2021 RC_END
ffff885f7edb8000 0000:00:04.3 0880 8086:2021 RC_END
crash> dev -i
RESOURCE RANGE NAME
ffffffff81a5bc80 0000-ffff PCI IO
ffff88303ff71140 0000-03af PCI Bus 0000:00
ffffffff81a40b20 0000-001f dma1
ffffffff81a40b58 0020-0021 pic1
5. dis 命令
dis命令可以输出反汇编
6. struct 命令
struct 命令可以显示结构的定义,并结合显示实际地址中的数据。
crash> struct timespec xtime_update
struct timespec {
tv_sec = -8554494033697431793,
tv_nsec = -4051189094619261979
}
-o可以显示结构体的偏移量
crash> struct -o transaction_s
struct transaction_s {
[0] journal_t *t_journal;
[8] tid_t t_tid;
[12] enum {T_RUNNING, T_LOCKED, T_FLUSH, T_COMMIT, T_COMMIT_DFLUSH, T_COMMIT_JFLUSH, T_COMMIT_CALLBACK, T_FINISHED} t_state;
[16] unsigned long t_log_start;
[24] int t_nr_buffers;
[32] struct journal_head *t_reserved_list;
[40] struct journal_head *t_buffers;
[48] struct journal_head *t_forget;
[56] struct journal_head *t_checkpoint_list;
[64] struct journal_head *t_checkpoint_io_list;
[72] struct journal_head *t_shadow_list;
[80] struct list_head t_inode_list;
[96] spinlock_t t_handle_lock;
[104] unsigned long t_max_wait;
[112] unsigned long t_start;
[120] unsigned long t_requested;
[128] struct transaction_chp_stats_s t_chp_stats;
[152] atomic_t t_updates;
[156] atomic_t t_outstanding_credits;
[160] transaction_t *t_cpnext;
[168] transaction_t *t_cpprev;
[176] unsigned long t_expires;
[184] ktime_t t_start_time;
[192] atomic_t t_handle_count;
[196] unsigned int t_synchronous_commit : 1;
[200] int t_need_data_flush;
[208] struct list_head t_private_list;
}
SIZE: 224
二、Crash的使用举例
用法1:查看数据成员的偏移,递归成员可以把偏移相加就行,也能侧得倒
crash> struct -o kvm_ioapic # 就可以看到数据结构内成员偏移
# 有了link的地址,怎么得到struct kvm_kernel_irq_routing_entry地址?那就要看该link在该数据结构中的偏移了。可以看到**偏移是48**
struct kvm_kernel_irq_routing_entry {
[48] struct hlist_node link; # 这里的48是10进制,要翻译成16进制才是偏移量
}
# 计算出该地址减去48后的地址量,注意是-48,而不是0x48。所以这里也可以直接减去0x30
p -x 0xffff88839fdfd3f0-48
# 也可以直接转换成16进制
crash> p -x 48
$1 = 0x30
用法2:怎么从栈中找传入的参数
起因:查看栈:发现spdk 的这个线程卡在sock_sendmsg上,所有要去排查到底是哪个socket。
unix_wait_for_peer(struct sock *other, long timeo)参数中有struct sock,从栈中查找参数地址,就能顺藤摸瓜struct socket。
内核栈的布局简单说明
栈内布局如下面,返回地址是上一帧的末尾,pre_rbp是这一帧的开头,后面是反汇编看到压入的寄存器,再后面是sub $0x30, %rsp留出的局部变量空间。注意,栈是从高地址往低地址去填入数据。注意:在x86-64 平台上,参数传递时如果子函数的参数不超过6个,那么直接使用寄存器传值,如果子函数的参数超过6个,那么从第七个参数开始,从后往前依次压入栈中。具体传第1、2、3、4、5参的寄存器分别是%rdi,%rsi,%rdx,%rcx,%rax。
用bt -f 命令查看一个具体示例:每个函数被调用,第一行指令就是push %rbp,注意点这个%rbp不是自己这个被调用者的栈顶地址,而是调用者的栈顶地址。这样当从被调用者return调用者时,只要pop %rbp就能回到调用者的栈顶。那么当被调用者去调用其他函数时,会先push %rip,然后执行call 指令。执行了call 指令就是进入了下一层栈帧了。由此可知,一个函数的栈帧数据,是以上一次调用者的栈顶地址%rbp为栈顶,以call 指令的下一行指令地址即返回地址%rip为栈底。
怎么从栈中找到参数