crash 工具使用

1. rd 命令

用法:

  • 读取内核虚地址或者内核符号值,默认16进制显示,类型为unsigned long,并且会将值对应的ascii码显示出来
    • rd <内核地址> 或 rd <内核符号>
    • 如果不需要将右边的ascii码显示出来,可以使用rd -x
    • 如果只显示ascii码的话,使用rd -a
  • 读取 物理内存地址的值
    •  rd -p 物理内存地址
  • 以10进制有符号数显示

    • rd -d

  • 以10进制无符号数显示

    • rd -D

  • 读取指定数量的内存,单位默认为unsigned long

    • rd <地址> <数量>

  • 指定数据宽度,默认是unsigned long,但是还可以指定别的,读取的数量的单位也会随之调整

        8 位宽:rd -8

        16位宽:rd -16

        32位宽:rd -32

        64位宽:rd -64

  • 将内存中的数据转换为可以识别的内存符号、函数指针、slab对象、文件指针等。
    • rd -s
    • 如果想得到详细的转换,那么可以用rd -S或者rd -SS
  • 将读到的区域dump到一个文件
    • rd -r <file>
  • 设置结束地址,如果不设置长度,那么一直读到设置的结束地址
    • rd -e <地址或符号>
  • 从指定偏移处开始读区,这个偏移是基于传入地址
    • rd -o <字节偏移>
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为栈底。


 

 怎么从栈中找到参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值