crash常用的調試命令

我的crash常用命令如下所示:

log/dmesg: 打印出故障現場的kmsg緩衝區log_buf中的內容。
struct:展示結構體的定義,或者從指定的地址開始解析一個結構體。
union:與struct類似,但是用於union的展示
p:print查看某個變量的值,實際上是調用gdb的p命令
whatis:展示結構體、聯合體等定義
bt <pid>:展示調用堆棧信息,如果不加參數那麼就可以利用SP和FP進行棧回溯打印;
-T顯示一個進程從thread_info以上一直到堆棧底部的所有symbol信息,一般比不加參數打印出的信息更多;
-a顯示所有active task的堆棧信息。
ps:展示系統中的進程狀態,和正常系統運行時的ps命令類似
task <pid>:展示某個pid的task_struct內容,不加pid則表示當前進程
dis <address>:反彙編命令,-l可以展示源代碼行。
mount:展示當前掛載的文件系統的命令
net:展示網絡相關的信息
rd <address>: read memory操作,讀取一個地址處的內容
file <pid>:查看某一個進程中的所有打開的文件
search -t <value/symbol>:在所有進程的stack頁面中查找一個value或者一個symbol,並打印出來結果

後面將主要記錄下實際的使用展示:

打印結構體和變量:

crash> union thread_union
union thread_union {
    unsigned long stack[2048];
}

除了顯示結構體內容,還可以利用後跟地址打印一個結構體或者聯合體的內容,比如:

crash> dis init_thread_union
0xffffff85e2720000 <.data>:     .inst   0x57ac6e9d ; undefined
crash> union thread_union 0xffffff85e2720000
union thread_union {
  stack = {1470918301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...}
}

我們想要利用union命令打印init_thread_union中的內容,先使用dis反彙編找到該變量的地址,然後再利用union後加地址打印出具體的內容。當然我們也可以利用後面介紹的p命令更容易的達到這個目的。

還是上面的例子,直接使用p命令打印一個全局變量:

crash> p init_thread_union
init_thread_union = $8 = {
  stack = {1470918301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...}
}

這種只適合我們知道一個變量的名字的情況,假如我們只知道變量類型和地址,那麼只能採用上面的union或者struct方式來獲取信息。

查看一個結構體定義:

crash> whatis thread_info
struct thread_info {
    unsigned long flags;
    mm_segment_t addr_limit;
    int preempt_count;
}
SIZE: 24

或者使用struct:

crash> struct thread_info
struct thread_info {
    unsigned long flags;
    mm_segment_t addr_limit;
    int preempt_count;
}
SIZE: 24

使用list打印一個鏈表:

crash> list file_system_type.next  0xffffff85e27833a0
ffffff85e27833a0
ffffff85e2740e78
ffffff85e278e9a0
ffffff85e27805f0
ffffff85e2782f30
ffffff85e27642a0
ffffff85e2762020
ffffff85e2762098
ffffff85e2771c20
ffffff85e2783508
ffffff85e278f408
ffffff85e278f450
ffffff85e28e5790
ffffff85e281ced8
ffffff85e276cdc8
ffffff85e277d6e0
ffffff85e2783638

list命令後面需要指定結構體中的next指針變量成員,最後再指定指針頭的address。該命令會把所有鏈表成員指針的地址都打印出來。加入我們想要以此打印鏈表中的某個成員的內容可以使用-s選項指定,比如:

crash> list file_system_type.next -s file_system_type.name 0xffffff85e27833a0
ffffff85e27833a0
  name = 0xffffff85e1f92148 "sysfs"
ffffff85e2740e78
  name = 0xffffff85e1d5c16d "rootfs"
ffffff85e278e9a0
  name = 0xffffff85e1d350d3 "ramfs"
ffffff85e27805f0
  name = 0xffffff85e1e8870c "bdev"
ffffff85e2782f30
  name = 0xffffff85e1f787b4 "proc"
ffffff85e27642a0
  name = 0xffffff85e1d56802 "cpuset"
ffffff85e2762020
  name = 0xffffff85e190bc40 "cgroup"
ffffff85e2762098
  name = 0xffffff85e1d49ee7 "cgroup2"
ffffff85e2771c20
  name = 0xffffff85e1d345f0 "tmpfs"
ffffff85e2783508
  name = 0xffffff85e1d61738 "configfs"
ffffff85e278f408
  name = 0xffffff85e1d56cb7 "debugfs"
ffffff85e278f450
  name = 0xffffff85e1d4c58f "tracefs"
ffffff85e28e5790
  name = 0xffffff85e1f884eb "sockfs"
ffffff85e281ced8
  name = 0xffffff85e1d67aef "dax"
ffffff85e276cdc8
  name = 0xffffff85e1d519ee "bpf"
ffffff85e277d6e0
  name = 0xffffff85e1d5b772 "pipefs"
ffffff85e2783638
  name = 0xffffff85e1d61b85 "devpts"
ffffff85e278ce00
  name = 0xffffff85e1d68bab "ext3"
ffffff85e278cdb8
  name = 0xffffff85e1d68b82 "ext2"
ffffff85e278cd70
  name = 0xffffff85e19315c0 "ext4"
ffffff85e278ea10
  name = 0xffffff85e1d6cba6 "vfat"
ffffff85e278ea58

查看init_task中的成員內容:

crash> dis init_task
0xffffff85e2740f00 <init_task>: .inst   0x00000022 ; undefined
crash> p 0xffffff85e2740f00
$11 = 18446743549227831040
crash> p *0xffffff85e2740f00
$12 = 34
crash> struct task_struct.comm,pid 0xffffff85e2740f00
  comm = "swapper/0\000\000\000\000\000\000"
  pid = 0

堆棧回溯分析:

crash> bt -T
PID: 574    TASK: ffffffff9fba8080  CPU: 4   COMMAND: "modprobe"
bt: WARNING: cannot determine starting stack frame for task ffffffff9fba8080
  [ffffff801b5cad10] save_trace at ffffff85e088aadc
  [ffffff801b5cad28] walk_stackframe at ffffff85e088aa10
  [ffffff801b5cad58] __save_stack_trace at ffffff85e088ac94
  [ffffff801b5cad68] __device_attach at ffffff85e0f41e84
  [ffffff801b5cadc0] __set_page_owner at ffffff85e0a560c0
  [ffffff801b5cadd8] save_stack at ffffff85e0a56198
  [ffffff801b5cae10] _raw_spin_unlock_irqrestore at ffffff85e18c8f4c
  [ffffff801b5cae28] return_address at ffffff85e088eb9c
  [ffffff801b5cae48] depot_save_stack at ffffff85e0c4c550
  [ffffff801b5cae50] _raw_spin_unlock_irqrestore at ffffff85e18c8f4c
  [ffffff801b5cae68] preempt_count_sub at ffffff85e08e5e10
  [ffffff801b5caf28] number at ffffff85e18bc248
  [ffffff801b5caf98] number at ffffff85e18bc248
  [ffffff801b5cafa8] number at ffffff85e18bc248
  [ffffff801b5cb018] vsnprintf at ffffff85e18bb2d4
  [ffffff801b5cb098] sprintf at ffffff85e18bc6a0
  [ffffff801b5cb158] vsnprintf at ffffff85e18bb2d4
  [ffffff801b5cb228] console_unlock at ffffff85e092b5f4
  [ffffff801b5cb2a8] vprintk_emit at ffffff85e092b094
  [ffffff801b5cb338] vprintk_default at ffffff85e092b7dc
  [ffffff801b5cb388] vprintk_func at ffffff85e092da58
  [ffffff801b5cb3d8] regmap_spmi_ext_write at ffffff85e0f6a970
  [ffffff801b5cb408] _regmap_raw_write at ffffff85e0f63f84
  [ffffff801b5cb4a8] printk at ffffff85e092a2fc

利用bt -T查看對應一個進程堆棧中的所有信息。當找到懷疑的函數之後,對照代碼查看可疑函數的實現,並且利用dis反彙編查看該函數具體的執行內容和運行地址:

crash> dis report_usercopy
0xffffff85e0a5bf00 <report_usercopy>:   stp     x22, x21, [sp,#-48]!
0xffffff85e0a5bf04 <report_usercopy+4>: stp     x20, x19, [sp,#16]
0xffffff85e0a5bf08 <report_usercopy+8>: stp     x29, x30, [sp,#32]
0xffffff85e0a5bf0c <report_usercopy+12>:        add     x29, sp, #0x20
0xffffff85e0a5bf10 <report_usercopy+16>:        mov     x19, x3
0xffffff85e0a5bf14 <report_usercopy+20>:        mov     w20, w2
0xffffff85e0a5bf18 <report_usercopy+24>:        mov     x21, x1
0xffffff85e0a5bf1c <report_usercopy+28>:        mov     x22, x0
0xffffff85e0a5bf20 <report_usercopy+32>:        nop
0xffffff85e0a5bf24 <report_usercopy+36>:        adrp    x8, 0xffffff85e1d4c000
0xffffff85e0a5bf28 <report_usercopy+40>:        adrp    x9, 0xffffff85e1d5b000
0xffffff85e0a5bf2c <report_usercopy+44>:        add     x8, x8, #0x4a5

之所以查看反彙編實現,是爲了找到和C語言代碼匹配的位置,進而定位出函數中的各個運行時參數值,並且在堆棧地址中找出來,有了這個參數,就可以帶入到函數找出出錯原因了。

查找功能
比如在堆棧中查找symbol:bug_handler

crash> search -t bug_handler
PID: 574    TASK: ffffffff9fba8080  CPU: 4   COMMAND: "modprobe"
ffffff801b5cb7f8: ffffff85e088b810 (bug_handler)

參考:

https://www.cnblogs.com/lshs/p/6113077.html
https://www.cnblogs.com/youngerchina/p/5624456.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章